api#326 Сохранить прежний random и чужие JSON токены
deploy-preland / deploy zoomstage.online (push) Successful in 45s
deploy-preland / deploy zoomstage.online (push) Successful in 45s
This commit is contained in:
1 parent
81e189ca67
commit
08febba368
4 files changed
+124
-35
No files matched your search
+85
-25
@@ -1,34 +1,94 @@
|
||||
<?php
|
||||
/** api#326: только согласованные имена/enum UTM; чужие поля/JSON-типы сохраняются. */
|
||||
/** api#326: заменяет только согласованные JSON-токены, сохраняя прочие байты. */
|
||||
declare(strict_types=1);
|
||||
|
||||
/** Карта диапазонов исходного JSON; числовые токены никогда не сериализуются. */
|
||||
function utmConfigNode(string $raw, int &$offset): array
|
||||
{
|
||||
$length=strlen($raw);
|
||||
while ($offset<$length && ctype_space($raw[$offset])) $offset++;
|
||||
$start=$offset; $char=$raw[$offset] ?? '';
|
||||
if ($char==='"') {
|
||||
$offset++;
|
||||
while ($offset<$length) {
|
||||
if ($raw[$offset]==='\\') { $offset+=2; continue; }
|
||||
if ($raw[$offset++]==='"') break;
|
||||
}
|
||||
return ['start'=>$start,'end'=>$offset,'type'=>'string','value'=>json_decode(substr($raw,$start,$offset-$start),true,512,JSON_THROW_ON_ERROR)];
|
||||
}
|
||||
if ($char==='{' || $char==='[') {
|
||||
$object=$char==='{'; $close=$object ? '}' : ']'; $offset++; $properties=[];
|
||||
while (true) {
|
||||
while ($offset<$length && ctype_space($raw[$offset])) $offset++;
|
||||
if (($raw[$offset] ?? '')===$close) { $offset++; break; }
|
||||
if ($object) {
|
||||
$key=utmConfigNode($raw,$offset);
|
||||
while ($offset<$length && ctype_space($raw[$offset])) $offset++;
|
||||
$offset++; // Двоеточие уже проверено полным JSON validation.
|
||||
$value=utmConfigNode($raw,$offset);
|
||||
$properties[]=['key'=>$key,'value'=>$value];
|
||||
} else { utmConfigNode($raw,$offset); }
|
||||
while ($offset<$length && ctype_space($raw[$offset])) $offset++;
|
||||
if (($raw[$offset] ?? '')===',') { $offset++; continue; }
|
||||
$offset++; break;
|
||||
}
|
||||
return ['start'=>$start,'end'=>$offset,'type'=>$object ? 'object' : 'array','properties'=>$properties];
|
||||
}
|
||||
while ($offset<$length && !str_contains(",]} \t\r\n",$raw[$offset])) $offset++;
|
||||
return ['start'=>$start,'end'=>$offset,'type'=>'scalar'];
|
||||
}
|
||||
|
||||
/** Выбирает единственное свойство; неоднозначные управляющие поля не мигрирует. */
|
||||
function utmConfigProperty(array $node, string $key): ?array
|
||||
{
|
||||
$found=null;
|
||||
foreach ($node['properties'] ?? [] as $property) {
|
||||
if ($property['key']['value']!==$key) continue;
|
||||
if ($found!==null) throw new RuntimeException('Повторное управляющее поле: '.$key);
|
||||
$found=$property;
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
|
||||
if ($argc!==2) { fwrite(STDERR,"Использование: php bin/ensure-utm-config.php config.json\n"); exit(2); }
|
||||
try {
|
||||
$path=$argv[1]; $raw=file_get_contents($path);
|
||||
$config=json_decode($raw,false,512,JSON_THROW_ON_ERROR);
|
||||
if (!$config instanceof stdClass || !($config->utm ?? null) instanceof stdClass) throw new RuntimeException('Отсутствует объект utm');
|
||||
$before=serialize($config);
|
||||
foreach (['registration'=>'reg','bchoice'=>'chob'] as $old=>$new) {
|
||||
if (property_exists($config->utm,$old)) {
|
||||
if (property_exists($config->utm,$new)) throw new RuntimeException('Конфликт старого и нового UTM: '.$new);
|
||||
$config->utm->$new=$config->utm->$old; unset($config->utm->$old);
|
||||
}
|
||||
}
|
||||
// Валидация всей грамматики без последующей сериализации decoded данных.
|
||||
json_decode($raw,false,512,JSON_THROW_ON_ERROR|JSON_BIGINT_AS_STRING);
|
||||
$offset=0; $root=utmConfigNode($raw,$offset);
|
||||
$utm=utmConfigProperty($root,'utm')['value'] ?? null;
|
||||
if (($root['type'] ?? null)!=='object' || ($utm['type'] ?? null)!=='object') throw new RuntimeException('Отсутствует объект utm');
|
||||
$replacements=[];
|
||||
$replace=static function(array $node,string $value) use (&$replacements): void {
|
||||
$replacements[]=['start'=>$node['start'],'end'=>$node['end'],'text'=>json_encode($value,JSON_THROW_ON_ERROR)];
|
||||
};
|
||||
foreach (['theme'=>['dark'=>'1','light'=>'2'],'reg'=>['before'=>'1','after'=>'2'],'chob'=>['b'=>'1','a'=>'2'],'focus'=>['face'=>'1','center'=>'2']] as $key=>$map) {
|
||||
if (!property_exists($config->utm,$key)) continue;
|
||||
$setting=$config->utm->$key;
|
||||
if (!$setting instanceof stdClass || !($setting->weights ?? null) instanceof stdClass || !(is_string($setting->default ?? null) || is_int($setting->default ?? null))) throw new RuntimeException('Некорректный UTM: '.$key);
|
||||
$weights=new stdClass;
|
||||
foreach ($setting->weights as $variant=>$weight) {
|
||||
$digit=$map[$variant] ?? (string)$variant;
|
||||
if (!in_array($digit,['1','2'],true) || property_exists($weights,$digit)) throw new RuntimeException('Неоднозначный вариант UTM: '.$key);
|
||||
$weights->$digit=$weight;
|
||||
$old=['reg'=>'registration','chob'=>'bchoice'][$key] ?? null;
|
||||
$property=utmConfigProperty($utm,$key);
|
||||
$legacy=$old===null ? null : utmConfigProperty($utm,$old);
|
||||
if ($property && $legacy) throw new RuntimeException('Конфликт старого и нового UTM: '.$key);
|
||||
if ($legacy) { $replace($legacy['key'],$key); $property=$legacy; }
|
||||
if (!$property) continue;
|
||||
$setting=$property['value'];
|
||||
$weights=utmConfigProperty($setting,'weights')['value'] ?? null;
|
||||
$default=utmConfigProperty($setting,'default')['value'] ?? null;
|
||||
if (($setting['type'] ?? null)!=='object' || ($weights['type'] ?? null)!=='object' || !$default) throw new RuntimeException('Некорректный UTM: '.$key);
|
||||
$seen=[];
|
||||
foreach ($weights['properties'] as $variant) {
|
||||
$name=$variant['key']['value']; $digit=$map[$name] ?? $name;
|
||||
if (!in_array($digit,['1','2'],true) || isset($seen[$digit])) throw new RuntimeException('Неоднозначный вариант UTM: '.$key);
|
||||
$seen[$digit]=true;
|
||||
if ($digit!==$name) $replace($variant['key'],$digit);
|
||||
}
|
||||
$default=$map[$setting->default] ?? $setting->default;
|
||||
if (!in_array((string)$default,['1','2'],true)) throw new RuntimeException('Некорректный default UTM: '.$key);
|
||||
$setting->weights=$weights; $setting->default=$default;
|
||||
$value=json_decode(substr($raw,$default['start'],$default['end']-$default['start']),false,512,JSON_THROW_ON_ERROR);
|
||||
if (!(is_string($value)||is_int($value))) throw new RuntimeException('Некорректный default UTM: '.$key);
|
||||
$digit=$map[$value] ?? $value;
|
||||
if (!in_array((string)$digit,['1','2'],true)) throw new RuntimeException('Некорректный default UTM: '.$key);
|
||||
if ($digit!==$value) $replace($default,$digit);
|
||||
}
|
||||
if (serialize($config)===$before) { echo "Числовые UTM уже настроены; config не изменён.\n"; exit(0); }
|
||||
$encoded=json_encode($config,JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES|JSON_THROW_ON_ERROR)."\n";
|
||||
if (file_put_contents($path,$encoded,LOCK_EX)===false) throw new RuntimeException('Не удалось записать config');
|
||||
echo "Мигрированы только согласованные имена и enum UTM.\n"; exit(10);
|
||||
if (!$replacements) { echo "Числовые UTM уже настроены; config не изменён.\n"; exit(0); }
|
||||
usort($replacements,static fn(array $a,array $b): int=>$b['start']<=>$a['start']);
|
||||
foreach ($replacements as $replacement) $raw=substr_replace($raw,$replacement['text'],$replacement['start'],$replacement['end']-$replacement['start']);
|
||||
if (file_put_contents($path,$raw,LOCK_EX)===false) throw new RuntimeException('Не удалось записать config');
|
||||
echo "Мигрированы только согласованные имена и enum UTM; остальные байты сохранены.\n"; exit(10);
|
||||
} catch(Throwable $error) { fwrite(STDERR,$error->getMessage()."\n"); exit(2); }
|
||||
Reference in new issue
Block a user