loadHTML('
'.str_replace(ArticleRecommendations::SLOT, '', $html).'
', LIBXML_NONET);
$root = $dom->getElementById('aid-root');
if (!$root) return null;
$xpath = new DOMXPath($dom);
$copies = $xpath->query('.//*[@data-clipboard-text]', $root);
if (!$copies || $copies->length % 2) return null;
// Empty historical updates are valid, but plaintext account rows must not disappear.
if (!$copies->length && (preg_match('/账号[::]|密码[::]/u', $root->textContent)
|| !$xpath->query('.//*[self::h1 or self::h2 or self::h3 or self::h4 or self::h5 or self::h6][starts-with(normalize-space(.), "免费苹果")]', $root)->length)) return null;
$result = ['accounts' => [], 'regions' => [], 'warnings' => [], 'intro' => '', 'outro' => ''];
$containers = [];
for ($i = 0; $i < $copies->length; $i += 2) {
$account = $copies->item($i);
$password = $copies->item($i + 1);
$container = $account->parentNode;
while ($container && $container !== $root && $container->parentNode !== $root) $container = $container->parentNode;
// Unknown nesting, labels or additional copy controls retain the legacy rendering.
if (!$container instanceof DOMElement || $container->parentNode !== $root || $container->tagName !== 'p') return null;
$fields = $xpath->query('.//*[@data-clipboard-text]', $container);
if ($fields->length !== 2 || !$fields->item(0)->isSameNode($account) || !$fields->item(1)->isSameNode($password)) return null;
$text = self::text($container->textContent);
if (!preg_match('/^账号[::]\s*.+[,,]\s*密码[::]\s*.+[,,]\s*地区[::]\s*(.*?)[,,]\s*检测时间[::]\s*(.*?)\s*$/u', $text, $match)) return null;
$username = $account->getAttribute('data-clipboard-text');
$secret = $password->getAttribute('data-clipboard-text');
if ($username === '' || $secret === '') return null;
$region = trim($match[1]);
if ($region === '') $region = '未注明';
$result['accounts'][] = ['account' => $username, 'password' => $secret, 'region' => $region, 'checked_at' => trim($match[2])];
$result['regions'][$region] = ($result['regions'][$region] ?? 0) + 1;
$containers[] = $container;
}
$seenAccount = false;
foreach (iterator_to_array($root->childNodes) as $node) {
$isAccount = false;
foreach ($containers as $container) if ($node->isSameNode($container)) { $isAccount = true; break; }
if ($isAccount) { $seenAccount = true; continue; }
$text = self::text($node->textContent);
if ($text === '') continue;
if ($node instanceof DOMElement && preg_match('/^免费苹果(?:账号|ID)/u', $text) && preg_match('/^h[1-6]$/', $node->tagName)) continue;
if (str_contains($text, '必须从App Store登录') && str_contains($text, 'iCloud')) {
$warning = preg_replace('/^[\x{26A0}\x{FE0F}\s]+/u', '', $text);
if (!in_array($warning, $result['warnings'], true)) $result['warnings'][] = $warning;
continue;
}
$result[$seenAccount ? 'outro' : 'intro'] .= $dom->saveHTML($node);
}
return $result;
} catch (\Throwable) {
return null;
} finally {
libxml_clear_errors();
libxml_use_internal_errors($before);
}
}
private static function text(string $text): string
{
return trim(preg_replace('/\s+/u', ' ', $text));
}
}