<?php

declare(strict_types=1);

// Exact-environment, read-only verifier for the ordering machine restaurant list.
$lane = (string)getenv('CDX_MACHINE_LANE');
$action = (string)($argv[1] ?? '');
$targets = [
    'A' => ['/tmp/zhct-http-store-a.l5omH8', 'http://127.0.0.1:18097', 21023, 'mysql'],
    'B' => ['/tmp/zhct-http-store-mysql.dQTTBP', 'http://127.0.0.1:18081', 10001, 'mysql'],
    'C' => ['/tmp/zhct-dm-poc-20260928.fcpiiN/store', 'http://127.0.0.1:18080', 112175, 'dm'],
];
if (!isset($targets[$lane]) || !in_array($action, ['--preflight', '--execute'], true)
    || ($action === '--execute'
        && ((string)getenv('CDX_MACHINE_EXECUTE') !== 'APPROVED_ORDER_MACHINE_READ_V1'
            || (string)getenv('CDX_MACHINE_SHA') !== hash_file('sha256', __FILE__)))) {
    fwrite(STDERR, "Machine verification disabled\n");
    exit(78);
}
[$root, $base, $pid, $dialect] = $targets[$lane];
if ((string)getenv('POC_APP_ROOT') !== $root
    || (string)getenv('POC_BASE_URL') !== $base
    || (string)getenv('POC_DIALECT') !== $dialect || is_link($root)
    || hash_file('sha256', $root . '/application/api/controller/OrderMachine.php')
        !== '7d54990994b6f6dbb36a4e66461f136c568d7c9a673a7d8741bc92b058fc11ca'
    || hash_file('sha256', $root . '/application/api/service/OrderMachineRestaurant.php')
        !== 'a3d58f51ec8eda71d6c8a64148b9dd9f556cdc67a3c945a61cbab240852b5d36'
    || hash_file('sha256', $root . '/application/common/Common.php')
        !== '49e2eab5fdc17e514f9420418b4cc281b92edb560b99f7cc62935011138dfeb4') {
    throw new RuntimeException('Approved machine source/runtime mismatch');
}
$runtimeReady = static function () use ($root, $base, $pid): bool {
    $cmdline = (string)@file_get_contents('/proc/' . $pid . '/cmdline');
    $environment = (string)@file_get_contents('/proc/' . $pid . '/environ');
    $children = (string)@file_get_contents('/proc/' . $pid . '/task/' . $pid . '/children');
    $boundary = json_decode((string)@file_get_contents($base . '/__poc_boundary'), true);
    $port = (int)parse_url($base, PHP_URL_PORT);
    $hex = strtoupper(str_pad(dechex($port), 4, '0', STR_PAD_LEFT));
    $inode = '';
    foreach (@file('/proc/net/tcp', FILE_IGNORE_NEW_LINES) ?: [] as $line) {
        $parts = preg_split('/\s+/', trim($line));
        if (($parts[1] ?? '') === '0100007F:' . $hex && ($parts[3] ?? '') === '0A') {
            $inode = (string)($parts[9] ?? '');
            break;
        }
    }
    $ownsPort = false;
    if ($inode !== '') {
        foreach (@scandir('/proc/' . $pid . '/fd') ?: [] as $fd) {
            if ($fd !== '.' && $fd !== '..'
                && @readlink('/proc/' . $pid . '/fd/' . $fd) === 'socket:[' . $inode . ']') {
                $ownsPort = true;
                break;
            }
        }
    }
    $multiWorker = preg_match('/(?:^|\x00)PHP_CLI_SERVER_WORKERS=(\d+)(?:\x00|$)/',
        $environment, $match) && (int)$match[1] > 1;
    return strpos($cmdline, '127.0.0.1:' . $port) !== false
        && strpos($cmdline, $root . '/public') !== false
        && $environment !== '' && trim($children) === '' && !$multiWorker
        && $ownsPort && is_array($boundary)
        && !empty($boundary['external_stubs'])
        && !empty($boundary['redis_isolated']) && empty($boundary['real_queue']);
};
if (!$runtimeReady()) throw new RuntimeException('Machine isolated HTTP boundary absent');
$runtime = (static function (string $path): array {
    $value = require $path;
    if (!is_array($value)) throw new RuntimeException('Invalid machine DB config');
    return $value;
})($root . '/application/database.php');
if ((string)($runtime['prefix'] ?? '') !== 'ydy_') {
    throw new RuntimeException('Machine table prefix mismatch');
}
if ($dialect === 'mysql') {
    $local = (static function (): array {
        $value = require '/workspace/zhct/zhctproject/store/application/config/local/database.php';
        if (!is_array($value)) throw new RuntimeException('Invalid code local DB config');
        return $value;
    })();
    if ((string)($runtime['type'] ?? '') !== 'mysql') {
        throw new RuntimeException('Machine MySQL driver mismatch');
    }
    foreach (['hostname', 'database', 'username', 'password'] as $key) {
        if (($runtime[$key] ?? null) !== ($local[$key] ?? null)) {
            throw new RuntimeException('Machine runtime differs from code local');
        }
    }
    if ((int)($runtime['hostport'] ?? 3306) !== (int)($local['hostport'] ?? 3306)) {
        throw new RuntimeException('Machine MySQL port mismatch');
    }
    $dsn = 'mysql:host=' . $runtime['hostname'] . ';port='
        . (int)($runtime['hostport'] ?? 3306) . ';dbname=' . $runtime['database']
        . ';charset=' . (string)($runtime['charset'] ?? 'utf8');
    $username = (string)$runtime['username'];
    $password = (string)$runtime['password'];
} else {
    if ((string)($runtime['type'] ?? '') !== 'app\\cores\\database\\Dm'
        || (string)getenv('POC_HOST') !== '127.0.0.1'
        || (string)getenv('POC_PORT') !== '5237'
        || strtoupper((string)getenv('POC_SCHEMA')) !== 'ZHCT_APP'
        || strtoupper((string)getenv('POC_USER')) !== 'ZHCT_APP') {
        throw new RuntimeException('Machine DM target mismatch');
    }
    foreach (['hostname' => 'POC_HOST', 'hostport' => 'POC_PORT',
        'database' => 'POC_SCHEMA', 'username' => 'POC_USER',
        'password' => 'POC_PASSWORD'] as $key => $envKey) {
        if ((string)($runtime[$key] ?? '') !== (string)getenv($envKey)) {
            throw new RuntimeException('Machine DM worker context mismatch');
        }
    }
    $dsn = 'dm:host=127.0.0.1;port=5237;schema=ZHCT_APP';
    $username = (string)getenv('POC_USER');
    $password = (string)getenv('POC_PASSWORD');
    $machineId = trim((string)@file_get_contents('/etc/machine-id'));
    if ($machineId === '' || gethostname() === false
        || hash('sha256', gethostname() . '|' . $machineId)
            !== 'd56d81988bb0f106946cce91c608fdd0ef1116e5150b23376ea87e086eace4b9') {
        throw new RuntimeException('Machine DM host mismatch');
    }
    $lines = [];
    $code = 0;
    @exec('/usr/sbin/ss -ltnp 2>/dev/null', $lines, $code);
    $localDm = false;
    if ($code === 0) {
        foreach ($lines as $line) {
            if (preg_match('/\*:5237\s+.*users:\(\("dmserver",pid=(\d+),/', $line, $match)) {
                $exe = @readlink('/proc/' . $match[1] . '/exe');
                $localDm = is_string($exe) && basename($exe) === 'dmserver';
                break;
            }
        }
    }
    if (!$localDm) throw new RuntimeException('Machine DM listener not local');
}
$pdo = new PDO($dsn, $username, $password, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_CASE => PDO::CASE_LOWER, PDO::ATTR_TIMEOUT => 5]);
if ($dialect === 'mysql') {
    $identity = (string)$pdo->query(
        "SELECT CONCAT(@@server_uuid,'|',@@hostname,'|',@@port,'|',DATABASE())"
    )->fetchColumn();
    $approved = '003eb5aecf1cd4595b609c1f7f20b9ee325b578309fb9fe14fedaf77584fa401';
} else {
    $schema = (string)$pdo->query(
        "SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL"
    )->fetchColumn();
    if (strtoupper($schema) !== 'ZHCT_APP') throw new RuntimeException('Machine DM schema mismatch');
    $identity = 'ZHCT_APP|5237|' . $schema;
    $approved = 'bc98485aed26beab348770a82ae38dbf922b04ab88a210cd7282c4caf0156194';
}
if (!hash_equals($approved, hash('sha256', $identity))) {
    throw new RuntimeException('Machine approved DB identity mismatch');
}
$prefix = $dialect === 'dm' ? 'YDY_' : 'ydy_';
$candidateSql = 'SELECT e.ID,e.CODE,e.RESTAURANT_ID,r.RESTAURANT_SHOP_ID,s.AREA_ID'
    . ' FROM ' . $prefix . 'equipment e'
    . ' INNER JOIN ' . $prefix . 'restaurants r ON r.ID=e.RESTAURANT_ID'
    . ' INNER JOIN ' . $prefix . 'restaurant_shop s ON s.ID=r.RESTAURANT_SHOP_ID'
    . ' INNER JOIN ' . $prefix . 'restaurant_area a ON a.ID=s.AREA_ID'
    . ' WHERE e.DEL_FLAG=0 AND r.IS_DELETE=2 AND r.STATUS=1'
    . ' AND s.IS_DELETE=0 AND s.STATUS=1 AND a.IS_DELETE=0 AND a.STATUS=1'
    . ' AND EXISTS (SELECT 1 FROM ' . $prefix . 'restaurants t'
    . ' INNER JOIN ' . $prefix . 'restaurant_shop ts ON ts.ID=t.RESTAURANT_SHOP_ID'
    . ' WHERE ts.AREA_ID=a.ID AND ts.IS_DELETE=0 AND ts.STATUS=1'
    . ' AND t.IS_DELETE=2 AND t.STATUS=1 AND t.STALL_TYPE=2)'
    . ' ORDER BY e.ID ASC';
$candidateStmt = $pdo->query($candidateSql);
$candidate = $candidateStmt->fetch(PDO::FETCH_ASSOC);
$candidateStmt->closeCursor();
$candidate = $candidate === false ? null : array_change_key_case($candidate, CASE_LOWER);
$areaId = $candidate === null ? 0 : (int)$candidate['area_id'];
$stallCount = 0;
$todayDishes = 0;
if ($areaId > 0) {
    $stmt = $pdo->prepare('SELECT COUNT(*) FROM ' . $prefix . 'restaurants t'
        . ' INNER JOIN ' . $prefix . 'restaurant_shop s ON s.ID=t.RESTAURANT_SHOP_ID'
        . ' WHERE s.AREA_ID=? AND s.IS_DELETE=0 AND s.STATUS=1'
        . ' AND t.IS_DELETE=2 AND t.STATUS=1 AND t.STALL_TYPE=2');
    $stmt->execute([$areaId]);
    $stallCount = (int)$stmt->fetchColumn();
    $stmt = $pdo->prepare('SELECT COUNT(DISTINCT r.DISHES_UUID) FROM ' . $prefix . 'recipes r'
        . ' INNER JOIN ' . $prefix . 'dishes d ON d.UUID=r.DISHES_UUID'
        . ' INNER JOIN ' . $prefix . 'restaurants t ON t.ID=r.RESTAURANT_ID'
        . ' INNER JOIN ' . $prefix . 'restaurant_shop s ON s.ID=t.RESTAURANT_SHOP_ID'
        . ' WHERE s.AREA_ID=? AND s.IS_DELETE=0 AND s.STATUS=1'
        . ' AND t.IS_DELETE=2 AND t.STATUS=1 AND t.STALL_TYPE=2'
        . ' AND r.MENU_DATE=? AND d.IS_DEL=1 AND d.STATUS=1');
    $stmt->execute([$areaId, date('Y-m-d')]);
    $todayDishes = (int)$stmt->fetchColumn();
}
$result = [
    'lane' => $lane, 'dialect' => $dialect, 'date' => date('Y-m-d'),
    'natural_valid_equipment_with_type2_area' => $candidate !== null,
    'candidate_code_sha' => $candidate === null ? null : hash('sha256', (string)$candidate['code']),
    'candidate_area_id' => $areaId,
    'active_type2_stalls_in_area' => $stallCount,
    'today_active_distinct_dishes' => $todayDishes,
    'http_executed' => false,
];
if ($action === '--preflight') {
    echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
    exit(0);
}
if ($candidate === null || $stallCount < 1) {
    throw new RuntimeException('No natural valid equipment/type-2 stall positive fixture');
}
date_default_timezone_set('PRC');
$toText = static function ($value): ?string {
    if ($value === null) return null;
    return is_resource($value) ? (string)stream_get_contents($value) : (string)$value;
};
$normalizeRows = static function ($stmt, int $limit) use ($toText): array {
    $rows = [];
    while (($row = $stmt->fetch(PDO::FETCH_ASSOC)) !== false) {
        $normal = [];
        foreach (array_change_key_case($row, CASE_LOWER) as $key => $value) {
            $normal[$key] = $toText($value);
        }
        $rows[] = $normal;
        if (count($rows) > $limit) throw new RuntimeException('Machine source bound exceeded');
    }
    return $rows;
};
$snapshot = static function () use ($pdo, $prefix, $candidate, $areaId,
    $normalizeRows): array {
    $stmt = $pdo->prepare('SELECT * FROM ' . $prefix . 'equipment WHERE ID=?');
    $stmt->execute([(int)$candidate['id']]);
    $equipment = $normalizeRows($stmt, 1);
    $stmt = $pdo->prepare('SELECT * FROM ' . $prefix . 'restaurant_area WHERE ID=?');
    $stmt->execute([$areaId]);
    $area = $normalizeRows($stmt, 1);
    $stmt = $pdo->prepare('SELECT * FROM ' . $prefix . 'restaurant_shop'
        . ' WHERE AREA_ID=? AND IS_DELETE=0 AND STATUS=1 ORDER BY ID ASC');
    $stmt->execute([$areaId]);
    $shops = $normalizeRows($stmt, 100);
    $stmt = $pdo->prepare('SELECT t.* FROM ' . $prefix . 'restaurants t'
        . ' INNER JOIN ' . $prefix . 'restaurant_shop s ON s.ID=t.RESTAURANT_SHOP_ID'
        . ' WHERE s.AREA_ID=? AND s.IS_DELETE=0 AND s.STATUS=1'
        . ' AND t.IS_DELETE=2 AND t.STATUS=1 AND t.STALL_TYPE=2'
        . ' ORDER BY s.ID ASC,t.ID ASC');
    $stmt->execute([$areaId]);
    $stalls = $normalizeRows($stmt, 100);
    $ids = array_map(static function ($row): int { return (int)$row['id']; }, $stalls);
    $recipes = [];
    if ($ids) {
        $placeholders = implode(',', array_fill(0, count($ids), '?'));
        $stmt = $pdo->prepare('SELECT r.ID,r.RESTAURANT_ID,r.MEAL_TIMES,r.DISHES_UUID,'
            . 'd.IS_DEL,d.STATUS FROM ' . $prefix . 'recipes r'
            . ' LEFT JOIN ' . $prefix . 'dishes d ON d.UUID=r.DISHES_UUID'
            . ' WHERE r.MENU_DATE=? AND r.RESTAURANT_ID IN (' . $placeholders . ')'
            . ' ORDER BY r.ID ASC');
        $stmt->execute(array_merge([date('Y-m-d')], $ids));
        $recipes = $normalizeRows($stmt, 3000);
    }
    if (count($equipment) !== 1 || count($area) !== 1
        || (string)$equipment[0]['code'] !== (string)$candidate['code']
        || (int)$equipment[0]['restaurant_id'] !== (int)$candidate['restaurant_id']
        || count($stalls) < 1) {
        throw new RuntimeException('Natural machine source drift');
    }
    return [$equipment, $area, $shops, $stalls, $recipes];
};
$sourceBefore = $snapshot();
$sourceSha = hash('sha256', serialize($sourceBefore));
[$equipmentRows, $areaRows, $shopRows, $stallRows, $recipeRows] = $sourceBefore;
$shopsById = [];
foreach ($shopRows as $shop) $shopsById[(int)$shop['id']] = $shop;
$stallsByShop = [];
foreach ($stallRows as $stall) {
    $shopId = (int)$stall['restaurant_shop_id'];
    $stallsByShop[$shopId][] = $stall;
}
$dishIds = [];
foreach ($recipeRows as $recipe) {
    if ((int)$recipe['is_del'] === 1 && (int)$recipe['status'] === 1) {
        $dishIds[(int)$recipe['restaurant_id']][(int)$recipe['meal_times']]
            [(string)$recipe['dishes_uuid']] = true;
    }
}
$mealAt = static function (array $stall, string $time): int {
    $clock = substr($time, 11, 8);
    foreach ([1 => ['morning_start', 'morning_end'],
        2 => ['noon_start', 'noon_end'], 3 => ['night_start', 'night_end']] as $meal => $fields) {
        $start = (string)($stall[$fields[0]] ?? '') . ':00';
        $end = (string)($stall[$fields[1]] ?? '') . ':59';
        if ($clock >= $start && $clock <= $end) return $meal;
    }
    return 4;
};
$httpCall = static function (array $form) use ($base, $runtimeReady): array {
    if (!$runtimeReady()) throw new RuntimeException('Machine runtime drift before HTTP');
    $body = http_build_query($form, '', '&', PHP_QUERY_RFC3986);
    $context = stream_context_create(['http' => ['method' => 'POST',
        'header' => "Content-Type: application/x-www-form-urlencoded\r\nAccept: application/json\r\n",
        'content' => $body, 'timeout' => 20, 'ignore_errors' => true]]);
    $start = microtime(true);
    $stream = @fopen($base . '/api/orderMachine/restaurantList', 'rb', false, $context);
    if (!$stream) throw new RuntimeException('Machine HTTP open failed');
    $headers = $http_response_header ?? [];
    $response = stream_get_contents($stream, 262145);
    fclose($stream);
    if (!is_string($response) || strlen($response) > 262144
        || strpos((string)($headers[0] ?? ''), ' 200 ') === false) {
        throw new RuntimeException('Machine HTTP status or response bound mismatch');
    }
    $json = json_decode($response, true);
    if (!is_array($json)) throw new RuntimeException('Machine HTTP JSON invalid');
    return [$json, (int)round((microtime(true) - $start) * 1000),
        strlen($response), hash('sha256', $response)];
};
$beforeAt = date('Y-m-d H:i:s');
[$positive, $positiveMs, $positiveBytes, $positiveSha] = $httpCall([
    'equipment_code' => (string)$candidate['code'],
]);
$afterAt = date('Y-m-d H:i:s');
if (substr($beforeAt, 0, 10) !== substr($afterAt, 0, 10)) {
    throw new RuntimeException('Machine test crossed date boundary');
}
if (array_keys($positive) !== ['code', 'message', 'data']
    || ($positive['code'] ?? null) !== 0
    || ($positive['message'] ?? null) !== '获取成功'
    || !is_array($positive['data'] ?? null)) {
    throw new RuntimeException('Machine positive envelope mismatch');
}
$data = $positive['data'];
if (array_keys($data) !== ['area_id', 'area_name', 'equipment', 'list']
    || !is_int($data['area_id']) || $data['area_id'] !== $areaId
    || !is_string($data['area_name']) || $data['area_name'] !== (string)$areaRows[0]['name']
    || !is_array($data['equipment'])
    || array_keys($data['equipment']) !== ['code', 'restaurant_id']
    || !is_string($data['equipment']['code'])
    || $data['equipment']['code'] !== (string)$candidate['code']
    || !is_int($data['equipment']['restaurant_id'])
    || $data['equipment']['restaurant_id'] !== (int)$candidate['restaurant_id']
    || !is_array($data['list']) || count($data['list']) < 1) {
    throw new RuntimeException('Machine positive area/equipment contract mismatch');
}
$expectedShopIds = [];
foreach ($shopRows as $shop) {
    if (!empty($stallsByShop[(int)$shop['id']])) $expectedShopIds[] = (int)$shop['id'];
}
if (count($expectedShopIds) !== count($data['list'])) {
    throw new RuntimeException('Machine positive shop count mismatch');
}
$stallJsonCount = 0;
$positiveDishCount = 0;
$mealNames = [1 => '早餐', 2 => '午餐', 3 => '晚餐', 4 => '夜宵'];
foreach ($data['list'] as $shopIndex => $shopJson) {
    $shopId = $expectedShopIds[$shopIndex];
    if (!is_array($shopJson)
        || array_keys($shopJson) !== ['shop_id', 'shop_name', 'stall_list']
        || !is_int($shopJson['shop_id']) || $shopJson['shop_id'] !== $shopId
        || !is_string($shopJson['shop_name'])
        || $shopJson['shop_name'] !== (string)$shopsById[$shopId]['name']
        || !is_array($shopJson['stall_list'])
        || count($shopJson['stall_list']) !== count($stallsByShop[$shopId])) {
        throw new RuntimeException('Machine positive shop/stall grouping mismatch');
    }
    foreach ($shopJson['stall_list'] as $stallIndex => $stallJson) {
        $source = $stallsByShop[$shopId][$stallIndex];
        $mealBefore = $mealAt($source, $beforeAt);
        $mealAfter = $mealAt($source, $afterAt);
        if ($mealBefore !== $mealAfter) {
            throw new RuntimeException('Machine test crossed meal boundary');
        }
        $id = (int)$source['id'];
        $dishesNum = count($dishIds[$id][$mealAfter] ?? []);
        $positiveDishCount += $dishesNum;
        $stallJsonCount++;
        if (!is_array($stallJson)
            || array_keys($stallJson) !== ['restaurant_id', 'restaurant_name',
                'file_path', 'intro', 'meal_date', 'meal_times', 'meal_name',
                'is_order', 'dishes_num']
            || !is_int($stallJson['restaurant_id']) || $stallJson['restaurant_id'] !== $id
            || !is_string($stallJson['restaurant_name'])
            || $stallJson['restaurant_name'] !== (string)$source['name']
            || !is_string($stallJson['file_path'])
            || !is_string($stallJson['intro'])
            || $stallJson['intro'] !== (string)$source['introduction']
            || !is_string($stallJson['meal_date'])
            || $stallJson['meal_date'] !== substr($afterAt, 0, 10)
            || !is_int($stallJson['meal_times']) || $stallJson['meal_times'] !== $mealAfter
            || !is_string($stallJson['meal_name'])
            || $stallJson['meal_name'] !== $mealNames[$mealAfter]
            || !is_int($stallJson['is_order'])
            || $stallJson['is_order'] !== ($dishesNum > 0 ? 1 : 2)
            || !is_int($stallJson['dishes_num'])
            || $stallJson['dishes_num'] !== $dishesNum) {
            throw new RuntimeException('Machine positive stall/recipe contract mismatch');
        }
        $path = str_replace('\\', '/', (string)($source['file_path'] ?? ''));
        if ($path !== '' && substr($stallJson['file_path'], -strlen($path)) !== $path) {
            throw new RuntimeException('Machine positive stall image source mismatch');
        }
    }
}
[$missing, $missingMs] = $httpCall([]);
if (array_keys($missing) !== ['code', 'message', 'data']
    || ($missing['code'] ?? null) !== 1
    || ($missing['message'] ?? null) !== '设备编号不能为空'
    || ($missing['data'] ?? null) !== []) {
    throw new RuntimeException('Machine missing-code rejection mismatch');
}
$unknownCode = 'CDX-NOT-FOUND-' . bin2hex(random_bytes(12));
$check = $pdo->prepare('SELECT COUNT(*) FROM ' . $prefix . 'equipment WHERE CODE=?');
$check->execute([$unknownCode]);
if ((int)$check->fetchColumn() !== 0) {
    throw new RuntimeException('Machine unknown-code fixture collision');
}
[$unknown, $unknownMs] = $httpCall(['equipment_code' => $unknownCode]);
if (array_keys($unknown) !== ['code', 'message', 'data']
    || ($unknown['code'] ?? null) !== 1
    || ($unknown['message'] ?? null) !== '设备不存在'
    || ($unknown['data'] ?? null) !== []) {
    throw new RuntimeException('Machine unknown-code rejection mismatch');
}
$sourceAfter = $snapshot();
if (!hash_equals($sourceSha, hash('sha256', serialize($sourceAfter)))) {
    throw new RuntimeException('Machine source changed during read-only HTTP');
}
$result['http_executed'] = true;
$result += [
    'positive_code' => 0, 'missing_code' => 1, 'unknown_code' => 1,
    'shop_count' => count($data['list']), 'stall_count' => $stallJsonCount,
    'current_meal_total_dishes' => $positiveDishCount,
    'source_sha' => $sourceSha, 'source_unchanged' => true,
    'response_bytes' => $positiveBytes, 'response_sha' => $positiveSha,
    'http_ms' => ['positive' => $positiveMs, 'missing' => $missingMs,
        'unknown' => $unknownMs],
    'json_types' => ['area_id' => gettype($data['area_id']),
        'equipment_code' => gettype($data['equipment']['code']),
        'equipment_restaurant_id' => gettype($data['equipment']['restaurant_id']),
        'shop_id' => gettype($data['list'][0]['shop_id']),
        'stall_id' => gettype($data['list'][0]['stall_list'][0]['restaurant_id']),
        'meal_times' => gettype($data['list'][0]['stall_list'][0]['meal_times']),
        'is_order' => gettype($data['list'][0]['stall_list'][0]['is_order']),
        'dishes_num' => gettype($data['list'][0]['stall_list'][0]['dishes_num'])],
];
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
