149 lines
4.0 KiB
PHP
Executable File
149 lines
4.0 KiB
PHP
Executable File
<?php
|
|
|
|
namespace Overtrue\Pinyin\Converters;
|
|
|
|
use Overtrue\Pinyin\Collection;
|
|
|
|
use function array_map;
|
|
use function mb_strlen;
|
|
use function mb_substr;
|
|
use function str_starts_with;
|
|
|
|
/**
|
|
* 智能版本的转换器
|
|
*
|
|
* 特点:
|
|
* - 根据文本长度智能选择策略
|
|
* - 短文本跳过不必要的长词词典
|
|
* - 缓存小型常用数据
|
|
* - 平衡内存和性能
|
|
*/
|
|
class SmartConverter extends AbstractConverter
|
|
{
|
|
private static ?array $surnamesCache = null;
|
|
|
|
private static array $segmentCache = [];
|
|
|
|
private static ?array $fullDictionary = null;
|
|
|
|
private const MAX_CACHE_SEGMENTS = 3;
|
|
|
|
public function convert(string $string): Collection
|
|
{
|
|
$string = $this->preprocessString($string);
|
|
|
|
return $this->determineConversionStrategy($string);
|
|
}
|
|
|
|
private function determineConversionStrategy(string $string): Collection
|
|
{
|
|
// 多音字处理
|
|
if ($this->heteronym) {
|
|
return $this->convertAsChars($string, true);
|
|
}
|
|
|
|
// 仅字符转换
|
|
if ($this->noWords) {
|
|
return $this->convertAsChars($string);
|
|
}
|
|
|
|
// 替换姓氏
|
|
if ($this->asSurname) {
|
|
$string = $this->convertSurname($string);
|
|
}
|
|
|
|
// 智能加载词典
|
|
$string = $this->smartConvert($string);
|
|
|
|
return $this->split($string);
|
|
}
|
|
|
|
private function smartConvert(string $string): string
|
|
{
|
|
// 使用和CachedConverter相同的逻辑,但保持缓存机制
|
|
$dictionary = $this->getFullDictionary();
|
|
|
|
return strtr($string, $dictionary);
|
|
}
|
|
|
|
private function getFullDictionary(): array
|
|
{
|
|
if (self::$fullDictionary === null) {
|
|
self::$fullDictionary = [];
|
|
// 按顺序加载,保证长词优先
|
|
foreach ($this->wordSegmentPaths() as $path) {
|
|
self::$fullDictionary += $this->loadSegmentWithCache($path);
|
|
}
|
|
}
|
|
|
|
return self::$fullDictionary;
|
|
}
|
|
|
|
private function loadSegmentWithCache(string $path): array
|
|
{
|
|
if (! isset(self::$segmentCache[$path])) {
|
|
// 缓存数量限制
|
|
if (count(self::$segmentCache) >= self::MAX_CACHE_SEGMENTS) {
|
|
// 移除最早的缓存(简单的 FIFO)
|
|
$firstKey = array_key_first(self::$segmentCache);
|
|
if ($firstKey !== null) {
|
|
unset(self::$segmentCache[$firstKey]);
|
|
}
|
|
}
|
|
self::$segmentCache[$path] = require $path;
|
|
}
|
|
|
|
return self::$segmentCache[$path];
|
|
}
|
|
|
|
protected function convertAsChars(string $string, bool $polyphonic = false): Collection
|
|
{
|
|
// 字符表太大,不缓存
|
|
$map = require self::CHARS_PATH;
|
|
|
|
$chars = mb_str_split($string);
|
|
$items = [];
|
|
|
|
foreach ($chars as $char) {
|
|
if (isset($map[$char])) {
|
|
if ($polyphonic) {
|
|
$pinyin = array_map(fn ($pinyin) => $this->formatTone($pinyin, $this->toneStyle->value), $map[$char]);
|
|
if ($this->heteronymAsList) {
|
|
$items[] = [$char => $pinyin];
|
|
} else {
|
|
$items[$char] = $pinyin;
|
|
}
|
|
} else {
|
|
$items[$char] = $this->formatTone($map[$char][0], $this->toneStyle->value);
|
|
}
|
|
}
|
|
}
|
|
|
|
return new Collection($items);
|
|
}
|
|
|
|
protected function convertSurname(string $name): string
|
|
{
|
|
// 姓氏表很小,可以缓存
|
|
self::$surnamesCache ??= require self::SURNAMES_PATH;
|
|
|
|
foreach (self::$surnamesCache as $surname => $pinyin) {
|
|
if (str_starts_with($name, $surname)) {
|
|
return $pinyin.mb_substr($name, mb_strlen($surname));
|
|
}
|
|
}
|
|
|
|
return $name;
|
|
}
|
|
|
|
/**
|
|
* 清理缓存
|
|
*/
|
|
public static function clearCache(): void
|
|
{
|
|
self::$surnamesCache = null;
|
|
self::$segmentCache = [];
|
|
self::$fullDictionary = null;
|
|
}
|
|
}
|