fix:修复BUG/升级1.1.6版本
This commit is contained in:
75
vendor/overtrue/pinyin/src/Collection.php
vendored
Normal file
75
vendor/overtrue/pinyin/src/Collection.php
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace Overtrue\Pinyin;
|
||||
|
||||
use ArrayAccess;
|
||||
use JsonSerializable;
|
||||
use Stringable;
|
||||
|
||||
class Collection implements ArrayAccess, JsonSerializable, Stringable
|
||||
{
|
||||
public function __construct(protected $items = [])
|
||||
{
|
||||
}
|
||||
|
||||
public function join(string $separator = ' '): string
|
||||
{
|
||||
return implode($separator, \array_map(function ($item) {
|
||||
return \is_array($item) ? '['.\implode(', ', $item).']' : $item;
|
||||
}, $this->items));
|
||||
}
|
||||
|
||||
public function map(callable $callback): Collection
|
||||
{
|
||||
return new static(array_map($callback, $this->all()));
|
||||
}
|
||||
|
||||
public function all(): array
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->all();
|
||||
}
|
||||
|
||||
public function toJson(int $options = 0): string
|
||||
{
|
||||
return json_encode($this->all(), $options);
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->join();
|
||||
}
|
||||
|
||||
public function offsetExists(mixed $offset): bool
|
||||
{
|
||||
return isset($this->items[$offset]);
|
||||
}
|
||||
|
||||
public function offsetGet(mixed $offset): mixed
|
||||
{
|
||||
return $this->items[$offset] ?? null;
|
||||
}
|
||||
|
||||
public function offsetSet(mixed $offset, mixed $value): void
|
||||
{
|
||||
if (null === $offset) {
|
||||
$this->items[] = $value;
|
||||
} else {
|
||||
$this->items[$offset] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function offsetUnset(mixed $offset): void
|
||||
{
|
||||
unset($this->items[$offset]);
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
}
|
||||
252
vendor/overtrue/pinyin/src/Converter.php
vendored
Normal file
252
vendor/overtrue/pinyin/src/Converter.php
vendored
Normal file
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
namespace Overtrue\Pinyin;
|
||||
|
||||
class Converter
|
||||
{
|
||||
private const SEGMENTS_COUNT = 10;
|
||||
private const WORDS_PATH = __DIR__.'/../data/words-%s.php';
|
||||
private const CHARS_PATH = __DIR__.'/../data/chars.php';
|
||||
private const SURNAMES_PATH = __DIR__.'/../data/surnames.php';
|
||||
|
||||
public const TONE_STYLE_SYMBOL = 'symbol';
|
||||
public const TONE_STYLE_NUMBER = 'number';
|
||||
public const TONE_STYLE_NONE = 'none';
|
||||
|
||||
protected bool $polyphonic = false;
|
||||
protected bool $asSurname = false;
|
||||
protected bool $noWords = false;
|
||||
|
||||
protected string $yuTo = 'yu';
|
||||
protected string $toneStyle = self::TONE_STYLE_SYMBOL;
|
||||
|
||||
protected array $regexps = [
|
||||
'separator' => '\p{Z}',
|
||||
'mark' => '\p{M}',
|
||||
'tab' => "\t"
|
||||
];
|
||||
|
||||
public const REGEXPS = [
|
||||
'number' => '0-9',
|
||||
'alphabet' => 'a-zA-Z',
|
||||
// 中文不带符号
|
||||
'hans' => '\x{3007}\x{2E80}-\x{2FFF}\x{3100}-\x{312F}\x{31A0}-\x{31EF}\x{3400}-\x{4DBF}\x{4E00}-\x{9FFF}\x{F900}-\x{FAFF}',
|
||||
// 符号: !"#$%&'()*+,-./:;<=>?@[\]^_{|}~`
|
||||
'punctuation' => '\p{P}',
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->regexps = \array_merge($this->regexps, self::REGEXPS);
|
||||
}
|
||||
|
||||
public static function make(): static
|
||||
{
|
||||
return new static();
|
||||
}
|
||||
|
||||
public function polyphonic(): static
|
||||
{
|
||||
$this->polyphonic = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function surname(): static
|
||||
{
|
||||
$this->asSurname = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function noWords(): static
|
||||
{
|
||||
$this->noWords = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function onlyHans(): static
|
||||
{
|
||||
// 中文汉字不含符号
|
||||
$this->regexps['hans'] = self::REGEXPS['hans'];
|
||||
|
||||
return $this->noAlpha()->noNumber()->noPunctuation();
|
||||
}
|
||||
|
||||
public function noAlpha(): static
|
||||
{
|
||||
unset($this->regexps['alphabet']);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function noNumber(): static
|
||||
{
|
||||
unset($this->regexps['number']);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function noPunctuation(): static
|
||||
{
|
||||
unset($this->regexps['punctuation']);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withToneStyle(string $toneStyle): static
|
||||
{
|
||||
$this->toneStyle = $toneStyle;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function noTone(): static
|
||||
{
|
||||
$this->toneStyle = self::TONE_STYLE_NONE;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function useNumberTone(): static
|
||||
{
|
||||
$this->toneStyle = self::TONE_STYLE_NUMBER;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function yuToV(): static
|
||||
{
|
||||
$this->yuTo = 'v';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function yuToU(): static
|
||||
{
|
||||
$this->yuTo = 'u';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function when(bool $condition, callable $callback): static
|
||||
{
|
||||
if ($condition) {
|
||||
$callback($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function convert(string $string, callable $beforeSplit = null): Collection
|
||||
{
|
||||
// 把原有的数字和汉字分离,避免拼音转换时被误作声调
|
||||
$string = preg_replace_callback('~[a-z0-9_-]+~i', function ($matches) {
|
||||
return "\t" . $matches[0];
|
||||
}, $string);
|
||||
|
||||
// 过滤掉不保留的字符
|
||||
$string = \preg_replace(\sprintf('~[^%s]~u', \implode($this->regexps)), '', $string);
|
||||
|
||||
// 多音字
|
||||
if ($this->polyphonic) {
|
||||
return $this->convertAsChars($string, true);
|
||||
}
|
||||
|
||||
if ($this->noWords) {
|
||||
return $this->convertAsChars($string);
|
||||
}
|
||||
|
||||
// 替换姓氏
|
||||
if ($this->asSurname) {
|
||||
$string = $this->convertSurname($string);
|
||||
}
|
||||
|
||||
for ($i = 0; $i < self::SEGMENTS_COUNT; $i++) {
|
||||
$string = strtr($string, require sprintf(self::WORDS_PATH, $i));
|
||||
}
|
||||
|
||||
return $this->split($beforeSplit ? $beforeSplit($string) : $string);
|
||||
}
|
||||
|
||||
public function convertAsChars(string $string, bool $polyphonic = false): Collection
|
||||
{
|
||||
$map = require self::CHARS_PATH;
|
||||
|
||||
// split string as chinese chars
|
||||
$chars = preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
$items = [];
|
||||
foreach ($chars as $char) {
|
||||
if (isset($map[$char])) {
|
||||
if ($polyphonic) {
|
||||
$items[$char] = \array_map(fn ($pinyin) => $this->formatTone($pinyin, $this->toneStyle), $map[$char]);
|
||||
} else {
|
||||
$items[$char] = $this->formatTone($map[$char][0], $this->toneStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Collection($items);
|
||||
}
|
||||
|
||||
protected function convertSurname(string $name): string
|
||||
{
|
||||
static $surnames = null;
|
||||
$surnames ??= require self::SURNAMES_PATH;
|
||||
|
||||
foreach ($surnames as $surname => $pinyin) {
|
||||
if (\str_starts_with($name, $surname)) {
|
||||
return $pinyin . \mb_substr($name, \mb_strlen($surname));
|
||||
}
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
protected function split(string $item): Collection
|
||||
{
|
||||
$items = \array_values(array_filter(preg_split('/\s+/i', $item)));
|
||||
|
||||
foreach ($items as $index => $item) {
|
||||
$items[$index] = $this->formatTone($item, $this->toneStyle);
|
||||
}
|
||||
|
||||
return new Collection($items);
|
||||
}
|
||||
|
||||
protected function formatTone(string $pinyin, string $style): string
|
||||
{
|
||||
$replacements = [
|
||||
'üē' => ['ue', 1], 'üé' => ['ue', 2], 'üě' => ['ue', 3], 'üè' => ['ue', 4],
|
||||
'ā' => ['a', 1], 'ē' => ['e', 1], 'ī' => ['i', 1], 'ō' => ['o', 1], 'ū' => ['u', 1], 'ǖ' => ['yu', 1],
|
||||
'á' => ['a', 2], 'é' => ['e', 2], 'í' => ['i', 2], 'ó' => ['o', 2], 'ú' => ['u', 2], 'ǘ' => ['yu', 2],
|
||||
'ǎ' => ['a', 3], 'ě' => ['e', 3], 'ǐ' => ['i', 3], 'ǒ' => ['o', 3], 'ǔ' => ['u', 3], 'ǚ' => ['yu', 3],
|
||||
'à' => ['a', 4], 'è' => ['e', 4], 'ì' => ['i', 4], 'ò' => ['o', 4], 'ù' => ['u', 4], 'ǜ' => ['yu', 4],
|
||||
];
|
||||
|
||||
foreach ($replacements as $unicode => $replacement) {
|
||||
if (\str_contains($pinyin, $unicode)) {
|
||||
$umlaut = $replacement[0];
|
||||
|
||||
if ($umlaut !== 'yu' && $style === self::TONE_STYLE_SYMBOL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// https://zh.wikipedia.org/wiki/%C3%9C
|
||||
if ($this->yuTo !== 'yu') {
|
||||
$umlaut = $this->yuTo;
|
||||
}
|
||||
|
||||
$pinyin = \str_replace($unicode, $umlaut, $pinyin);
|
||||
|
||||
if ($this->toneStyle === self::TONE_STYLE_NUMBER) {
|
||||
$pinyin .= $replacement[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $pinyin;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/pinyin.
|
||||
*
|
||||
* (c) overtrue <i@overtrue.me>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace Overtrue\Pinyin;
|
||||
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* Dict loader interface.
|
||||
*/
|
||||
interface DictLoaderInterface
|
||||
{
|
||||
/**
|
||||
* Load dict.
|
||||
*
|
||||
* <pre>
|
||||
* [
|
||||
* '响应时间' => "[\t]xiǎng[\t]yìng[\t]shí[\t]jiān",
|
||||
* '长篇连载' => '[\t]cháng[\t]piān[\t]lián[\t]zǎi',
|
||||
* //...
|
||||
* ]
|
||||
* </pre>
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function map(Closure $callback);
|
||||
|
||||
/**
|
||||
* Load surname dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function mapSurname(Closure $callback);
|
||||
}
|
||||
73
vendor/overtrue/pinyin/src/FileDictLoader.php
vendored
73
vendor/overtrue/pinyin/src/FileDictLoader.php
vendored
@@ -1,73 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/pinyin.
|
||||
*
|
||||
* (c) overtrue <i@overtrue.me>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace Overtrue\Pinyin;
|
||||
|
||||
use Closure;
|
||||
|
||||
class FileDictLoader implements DictLoaderInterface
|
||||
{
|
||||
/**
|
||||
* Words segment name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $segmentName = 'words_%s';
|
||||
|
||||
/**
|
||||
* Dict path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function __construct($path)
|
||||
{
|
||||
$this->path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function map(Closure $callback)
|
||||
{
|
||||
for ($i = 0; $i < 100; ++$i) {
|
||||
$segment = $this->path . '/' . sprintf($this->segmentName, $i);
|
||||
|
||||
if (file_exists($segment)) {
|
||||
$dictionary = (array) include $segment;
|
||||
$callback($dictionary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load surname dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function mapSurname(Closure $callback)
|
||||
{
|
||||
$surnames = $this->path . '/surnames';
|
||||
|
||||
if (file_exists($surnames)) {
|
||||
$dictionary = (array) include $surnames;
|
||||
$callback($dictionary);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/pinyin.
|
||||
*
|
||||
* (c) overtrue <i@overtrue.me>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace Overtrue\Pinyin;
|
||||
|
||||
use Closure;
|
||||
use SplFileObject;
|
||||
use Generator;
|
||||
|
||||
class GeneratorFileDictLoader implements DictLoaderInterface
|
||||
{
|
||||
/**
|
||||
* Data directory.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* Words segment name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $segmentName = 'words_%s';
|
||||
|
||||
/**
|
||||
* SplFileObjects.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $handles = [];
|
||||
|
||||
/**
|
||||
* surnames.
|
||||
*
|
||||
* @var SplFileObject
|
||||
*/
|
||||
protected static $surnamesHandle;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function __construct($path)
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
for ($i = 0; $i < 100; ++$i) {
|
||||
$segment = $this->path . '/' . sprintf($this->segmentName, $i);
|
||||
|
||||
if (file_exists($segment) && is_file($segment)) {
|
||||
array_push(static::$handles, $this->openFile($segment));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new file object.
|
||||
*
|
||||
* @param string $filename file path
|
||||
* @param string $mode file open mode
|
||||
*
|
||||
* @return SplFileObject
|
||||
*/
|
||||
protected function openFile($filename, $mode = 'r')
|
||||
{
|
||||
return new SplFileObject($filename, $mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* get Generator syntax.
|
||||
*
|
||||
* @param array $handles SplFileObjects
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
protected function getGenerator(array $handles)
|
||||
{
|
||||
foreach ($handles as $handle) {
|
||||
$handle->seek(0);
|
||||
while (false === $handle->eof()) {
|
||||
$string = str_replace(['\'', ' ', PHP_EOL, ','], '', $handle->fgets());
|
||||
|
||||
if (false === strpos($string, '=>')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
list($string, $pinyin) = explode('=>', $string);
|
||||
|
||||
yield $string => $pinyin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse the stream.
|
||||
*
|
||||
* @param Generator $generator
|
||||
* @param Closure $callback
|
||||
*
|
||||
* @author Seven Du <shiweidu@outlook.com>
|
||||
*/
|
||||
protected function traversing(Generator $generator, Closure $callback)
|
||||
{
|
||||
foreach ($generator as $string => $pinyin) {
|
||||
$callback([$string => $pinyin]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function map(Closure $callback)
|
||||
{
|
||||
$this->traversing($this->getGenerator(static::$handles), $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load surname dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function mapSurname(Closure $callback)
|
||||
{
|
||||
if (!static::$surnamesHandle instanceof SplFileObject) {
|
||||
static::$surnamesHandle = $this->openFile($this->path . '/surnames');
|
||||
}
|
||||
|
||||
$this->traversing($this->getGenerator([static::$surnamesHandle]), $callback);
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/pinyin.
|
||||
*
|
||||
* (c) overtrue <i@overtrue.me>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace Overtrue\Pinyin;
|
||||
|
||||
use Closure;
|
||||
|
||||
class MemoryFileDictLoader implements DictLoaderInterface
|
||||
{
|
||||
/**
|
||||
* Data directory.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* Words segment name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $segmentName = 'words_%s';
|
||||
|
||||
/**
|
||||
* Segment files.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $segments = [];
|
||||
|
||||
/**
|
||||
* Surname cache.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $surnames = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function __construct($path)
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
for ($i = 0; $i < 100; ++$i) {
|
||||
$segment = $path . '/' . sprintf($this->segmentName, $i);
|
||||
|
||||
if (file_exists($segment)) {
|
||||
$this->segments[] = (array) include $segment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function map(Closure $callback)
|
||||
{
|
||||
foreach ($this->segments as $dictionary) {
|
||||
$callback($dictionary);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load surname dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function mapSurname(Closure $callback)
|
||||
{
|
||||
if (empty($this->surnames)) {
|
||||
$surnames = $this->path . '/surnames';
|
||||
|
||||
if (file_exists($surnames)) {
|
||||
$this->surnames = (array) include $surnames;
|
||||
}
|
||||
}
|
||||
|
||||
$callback($this->surnames);
|
||||
}
|
||||
}
|
||||
366
vendor/overtrue/pinyin/src/Pinyin.php
vendored
366
vendor/overtrue/pinyin/src/Pinyin.php
vendored
@@ -1,341 +1,85 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/pinyin.
|
||||
*
|
||||
* (c) overtrue <i@overtrue.me>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace Overtrue\Pinyin;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* @method static Converter polyphonic()
|
||||
* @method static Converter surname()
|
||||
* @method static Converter noWords()
|
||||
* @method static Converter onlyHans()
|
||||
* @method static Converter noAlpha()
|
||||
* @method static Converter noNumber()
|
||||
* @method static Converter noPunctuation()
|
||||
* @method static Converter noTone()
|
||||
* @method static Converter useNumberTone()
|
||||
* @method static Converter yuToV()
|
||||
* @method static Converter yuToU()
|
||||
* @method static Converter withToneStyle(string $toneStyle = 'symbol')
|
||||
* @method static Collection convert(string $string, callable $beforeSplit = null)
|
||||
*/
|
||||
class Pinyin
|
||||
{
|
||||
/**
|
||||
* Dict loader.
|
||||
*
|
||||
* @var \Overtrue\Pinyin\DictLoaderInterface
|
||||
*/
|
||||
protected $loader;
|
||||
|
||||
/**
|
||||
* Punctuations map.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $punctuations = [
|
||||
',' => ',',
|
||||
'。' => '.',
|
||||
'!' => '!',
|
||||
'?' => '?',
|
||||
':' => ':',
|
||||
'“' => '"',
|
||||
'”' => '"',
|
||||
'‘' => "'",
|
||||
'’' => "'",
|
||||
'_' => '_',
|
||||
];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $loaderName
|
||||
*/
|
||||
public function __construct($loaderName = null)
|
||||
public static function name(string $name, string $toneStyle = Converter::TONE_STYLE_SYMBOL): Collection
|
||||
{
|
||||
$this->loader = $loaderName ?: 'Overtrue\\Pinyin\\FileDictLoader';
|
||||
return self::surname()->withToneStyle($toneStyle)->convert($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string to pinyin.
|
||||
*
|
||||
* @param string $string
|
||||
* @param int $option
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function convert($string, $option = PINYIN_DEFAULT)
|
||||
public static function phrase(string $string, string $toneStyle = Converter::TONE_STYLE_SYMBOL): Collection
|
||||
{
|
||||
$pinyin = $this->romanize($string, $option);
|
||||
|
||||
return $this->splitWords($pinyin, $option);
|
||||
return self::noPunctuation()->withToneStyle($toneStyle)->convert($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string (person name) to pinyin.
|
||||
*
|
||||
* @param string $stringName
|
||||
* @param int $option
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function name($stringName, $option = PINYIN_NAME)
|
||||
public static function sentence(string $string, string $toneStyle = Converter::TONE_STYLE_SYMBOL): Collection
|
||||
{
|
||||
$option = $option | PINYIN_NAME;
|
||||
|
||||
$pinyin = $this->romanize($stringName, $option);
|
||||
|
||||
return $this->splitWords($pinyin, $option);
|
||||
return self::withToneStyle($toneStyle)->convert($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a pinyin permalink from string.
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $delimiter
|
||||
* @param int $option
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function permalink($string, $delimiter = '-', $option = PINYIN_DEFAULT)
|
||||
public static function polyphones(string $string, string $toneStyle = Converter::TONE_STYLE_SYMBOL): Collection
|
||||
{
|
||||
if (\is_int($delimiter)) {
|
||||
list($option, $delimiter) = [$delimiter, '-'];
|
||||
}
|
||||
return self::polyphonic()->withToneStyle($toneStyle)->convert($string);
|
||||
}
|
||||
|
||||
public static function chars(string $string, string $toneStyle = Converter::TONE_STYLE_SYMBOL): Collection
|
||||
{
|
||||
return self::onlyHans()->noWords()->withToneStyle($toneStyle)->convert($string);
|
||||
}
|
||||
|
||||
public static function permalink(string $string, string $delimiter = '-'): string
|
||||
{
|
||||
if (!in_array($delimiter, ['_', '-', '.', ''], true)) {
|
||||
throw new InvalidArgumentException("Delimiter must be one of: '_', '-', '', '.'.");
|
||||
}
|
||||
|
||||
return implode($delimiter, $this->convert($string, $option | \PINYIN_KEEP_NUMBER | \PINYIN_KEEP_ENGLISH));
|
||||
return self::noPunctuation()->noTone()->convert($string)->join($delimiter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return first letters.
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $delimiter
|
||||
* @param int $option
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function abbr($string, $delimiter = '', $option = PINYIN_DEFAULT)
|
||||
public static function nameAbbr(string $string): Collection
|
||||
{
|
||||
if (\is_int($delimiter)) {
|
||||
list($option, $delimiter) = [$delimiter, ''];
|
||||
return self::abbr($string, true);
|
||||
}
|
||||
|
||||
public static function abbr(string $string, bool $asName = false): Collection
|
||||
{
|
||||
return self::noTone()
|
||||
->noPunctuation()
|
||||
->when($asName, fn ($c) => $c->surname())
|
||||
->convert($string)
|
||||
->map(function ($pinyin) {
|
||||
// 常用于电影名称入库索引处理,例如:《晚娘2012》-> WN2012
|
||||
return \is_numeric($pinyin) || preg_match('/\d{2,}/', $pinyin) ? $pinyin : \mb_substr($pinyin, 0, 1);
|
||||
});
|
||||
}
|
||||
|
||||
public static function __callStatic(string $name, array $arguments)
|
||||
{
|
||||
$converter = Converter::make();
|
||||
|
||||
if (\method_exists($converter, $name)) {
|
||||
return $converter->$name(...$arguments);
|
||||
}
|
||||
|
||||
return implode($delimiter, array_map(function ($pinyin) {
|
||||
return \is_numeric($pinyin) || preg_match('/\d+/', $pinyin) ? $pinyin : mb_substr($pinyin, 0, 1);
|
||||
}, $this->convert($string, $option | PINYIN_NO_TONE)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Chinese phrase to pinyin.
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $delimiter
|
||||
* @param int $option
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function phrase($string, $delimiter = ' ', $option = PINYIN_DEFAULT)
|
||||
{
|
||||
if (\is_int($delimiter)) {
|
||||
list($option, $delimiter) = [$delimiter, ' '];
|
||||
}
|
||||
|
||||
return implode($delimiter, $this->convert($string, $option));
|
||||
}
|
||||
|
||||
/**
|
||||
* Chinese to pinyin sentence.
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $delimiter
|
||||
* @param int $option
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function sentence($string, $delimiter = ' ', $option = \PINYIN_NO_TONE)
|
||||
{
|
||||
if (\is_int($delimiter)) {
|
||||
list($option, $delimiter) = [$delimiter, ' '];
|
||||
}
|
||||
|
||||
return implode($delimiter, $this->convert($string, $option | \PINYIN_KEEP_PUNCTUATION | \PINYIN_KEEP_ENGLISH | \PINYIN_KEEP_NUMBER));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loader setter.
|
||||
*
|
||||
* @param \Overtrue\Pinyin\DictLoaderInterface $loader
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLoader(DictLoaderInterface $loader)
|
||||
{
|
||||
$this->loader = $loader;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return dict loader,.
|
||||
*
|
||||
* @return \Overtrue\Pinyin\DictLoaderInterface
|
||||
*/
|
||||
public function getLoader()
|
||||
{
|
||||
if (!($this->loader instanceof DictLoaderInterface)) {
|
||||
$dataDir = dirname(__DIR__) . '/data/';
|
||||
|
||||
$loaderName = $this->loader;
|
||||
$this->loader = new $loaderName($dataDir);
|
||||
}
|
||||
|
||||
return $this->loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Chinese to pinyin.
|
||||
*
|
||||
* @param string $string
|
||||
* @param int $option
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function romanize($string, $option = \PINYIN_DEFAULT)
|
||||
{
|
||||
$string = $this->prepare($string, $option);
|
||||
|
||||
$dictLoader = $this->getLoader();
|
||||
|
||||
if ($this->hasOption($option, \PINYIN_NAME)) {
|
||||
$string = $this->convertSurname($string, $dictLoader);
|
||||
}
|
||||
|
||||
$dictLoader->map(function ($dictionary) use (&$string) {
|
||||
$string = strtr($string, $dictionary);
|
||||
});
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Chinese Surname to pinyin.
|
||||
*
|
||||
* @param string $string
|
||||
* @param \Overtrue\Pinyin\DictLoaderInterface $dictLoader
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function convertSurname($string, $dictLoader)
|
||||
{
|
||||
$dictLoader->mapSurname(function ($dictionary) use (&$string) {
|
||||
foreach ($dictionary as $surname => $pinyin) {
|
||||
if (0 === strpos($string, $surname)) {
|
||||
$string = $pinyin . mb_substr($string, mb_strlen($surname, 'UTF-8'), mb_strlen($string, 'UTF-8') - 1, 'UTF-8');
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split pinyin string to words.
|
||||
*
|
||||
* @param string $pinyin
|
||||
* @param string $option
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function splitWords($pinyin, $option)
|
||||
{
|
||||
$split = array_filter(preg_split('/\s+/i', $pinyin));
|
||||
|
||||
if (!$this->hasOption($option, PINYIN_TONE)) {
|
||||
foreach ($split as $index => $pinyin) {
|
||||
$split[$index] = $this->formatTone($pinyin, $option);
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($split);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $option
|
||||
* @param int $check
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasOption($option, $check)
|
||||
{
|
||||
return ($option & $check) === $check;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-process.
|
||||
*
|
||||
* @param string $string
|
||||
* @param int $option
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function prepare($string, $option = \PINYIN_DEFAULT)
|
||||
{
|
||||
$string = preg_replace_callback('~[a-z0-9_-]+~i', function ($matches) {
|
||||
return "\t" . $matches[0];
|
||||
}, $string);
|
||||
|
||||
$regex = ['\p{Han}', '\p{Z}', '\p{M}', "\t"];
|
||||
|
||||
if ($this->hasOption($option, \PINYIN_KEEP_NUMBER)) {
|
||||
\array_push($regex, '0-9');
|
||||
}
|
||||
|
||||
if ($this->hasOption($option, \PINYIN_KEEP_ENGLISH)) {
|
||||
\array_push($regex, 'a-zA-Z');
|
||||
}
|
||||
|
||||
if ($this->hasOption($option, \PINYIN_KEEP_PUNCTUATION)) {
|
||||
$punctuations = array_merge($this->punctuations, ["\t" => ' ', ' ' => ' ']);
|
||||
$string = trim(str_replace(array_keys($punctuations), $punctuations, $string));
|
||||
|
||||
\array_push($regex, preg_quote(implode(array_merge(array_keys($this->punctuations), $this->punctuations)), '~'));
|
||||
}
|
||||
|
||||
return preg_replace(\sprintf('~[^%s]~u', implode($regex)), '', $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format.
|
||||
*
|
||||
* @param string $pinyin
|
||||
* @param int $option
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function formatTone($pinyin, $option = \PINYIN_NO_TONE)
|
||||
{
|
||||
$replacements = [
|
||||
'üē' => ['ue', 1], 'üé' => ['ue', 2], 'üě' => ['ue', 3], 'üè' => ['ue', 4],
|
||||
'ā' => ['a', 1], 'ē' => ['e', 1], 'ī' => ['i', 1], 'ō' => ['o', 1], 'ū' => ['u', 1], 'ǖ' => ['yu', 1],
|
||||
'á' => ['a', 2], 'é' => ['e', 2], 'í' => ['i', 2], 'ó' => ['o', 2], 'ú' => ['u', 2], 'ǘ' => ['yu', 2],
|
||||
'ǎ' => ['a', 3], 'ě' => ['e', 3], 'ǐ' => ['i', 3], 'ǒ' => ['o', 3], 'ǔ' => ['u', 3], 'ǚ' => ['yu', 3],
|
||||
'à' => ['a', 4], 'è' => ['e', 4], 'ì' => ['i', 4], 'ò' => ['o', 4], 'ù' => ['u', 4], 'ǜ' => ['yu', 4],
|
||||
];
|
||||
|
||||
foreach ($replacements as $unicode => $replacement) {
|
||||
if (false !== strpos($pinyin, $unicode)) {
|
||||
$umlaut = $replacement[0];
|
||||
|
||||
// https://zh.wikipedia.org/wiki/%C3%9C
|
||||
if ($this->hasOption($option, \PINYIN_UMLAUT_V) && 'yu' == $umlaut) {
|
||||
$umlaut = 'v';
|
||||
}
|
||||
|
||||
$pinyin = str_replace($unicode, $umlaut, $pinyin) . ($this->hasOption($option, PINYIN_ASCII_TONE) ? $replacement[1] : '');
|
||||
}
|
||||
}
|
||||
|
||||
return $pinyin;
|
||||
throw new InvalidArgumentException("Method {$name} does not exist.");
|
||||
}
|
||||
}
|
||||
|
||||
20
vendor/overtrue/pinyin/src/const.php
vendored
20
vendor/overtrue/pinyin/src/const.php
vendored
@@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/pinyin.
|
||||
*
|
||||
* (c) overtrue <i@overtrue.me>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
define('PINYIN_DEFAULT', 4096);
|
||||
define('PINYIN_TONE', 2);
|
||||
define('PINYIN_NO_TONE', 4);
|
||||
define('PINYIN_ASCII_TONE', 8);
|
||||
define('PINYIN_NAME', 16);
|
||||
define('PINYIN_KEEP_NUMBER', 32);
|
||||
define('PINYIN_KEEP_ENGLISH', 64);
|
||||
define('PINYIN_UMLAUT_V', 128);
|
||||
define('PINYIN_KEEP_PUNCTUATION', 256);
|
||||
Reference in New Issue
Block a user