本文目录导读:

我来给你提供几种PHP拼音搜索的解决方案,从简单到复杂都有:
最简方案:使用拼音数据库(推荐)
安装方式
composer require overtrue/pinyin
核心实现代码
<?php
use Overtrue\Pinyin\Pinyin;
class PinyinSearch
{
private $pinyin;
public function __construct()
{
$this->pinyin = new Pinyin();
}
/**
* 将中文转拼音
*/
public function toPinyin($text)
{
// 获取全拼
$full = $this->pinyin->convert($text);
// 获取首字母
$abbr = $this->pinyin->abbr($text);
return [
'full' => implode('', $full), // 全拼无空格
'full_space' => implode(' ', $full), // 全拼带空格
'abbr' => $abbr, // 首字母
];
}
/**
* 搜索方法 - 支持拼音和中文混合搜索
*/
public function search($keywords, $data)
{
$results = [];
$kwPinyin = $this->toPinyin($keywords);
foreach ($data as $item) {
$itemPinyin = $this->toPinyin($item['name']);
// 多种匹配方式
if (strpos($itemPinyin['full'], $keywords) !== false || // 直接匹配中文
strpos($itemPinyin['full'], $kwPinyin['full']) !== false || // 匹配全拼
strpos($itemPinyin['abbr'], $kwPinyin['abbr']) !== false || // 匹配首字母
$this->fuzzyMatch($keywords, $itemPinyin)) { // 模糊匹配
$results[] = $item;
}
}
return $results;
}
/**
* 模糊匹配(支持拼音简拼和全拼混合)
*/
private function fuzzyMatch($keyword, $pinyinData)
{
$keyword = strtolower($keyword);
$full = strtolower($pinyinData['full']);
$abbr = strtolower($pinyinData['abbr']);
// 简拼+全拼混合,如 "北京欢迎你" -> "bjhyn"
if (preg_match('/^[a-z]+$/', $keyword)) {
// 全字母匹配
if (strpos($full, $keyword) !== false) return true;
// 首字母匹配
if (strpos($abbr, $keyword) !== false) return true;
// 混合匹配:部分全拼+部分简拼
$pattern = '';
foreach (str_split($keyword) as $char) {
$pattern .= $char . '.*';
}
if (preg_match('/' . $pattern . '/', $full)) return true;
}
return false;
}
}
// 使用示例
$search = new PinyinSearch();
$data = [
['id' => 1, 'name' => '北京欢迎你'],
['id' => 2, 'name' => '上海迪士尼'],
['id' => 3, 'name' => '广州长隆'],
['id' => 4, 'name' => '深圳世界之窗'],
];
// 搜索 "北京" 或 "bj" 或 "北京hyn"
$results = $search->search('bj', $data);
print_r($results);
MySQL全文索引方案(大数据量)
数据库表结构设计
-- 创建拼音字段表 CREATE TABLE `products` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `pinyin` varchar(255) DEFAULT NULL, -- 全拼 `pinyin_abbr` varchar(50) DEFAULT NULL, -- 首字母 PRIMARY KEY (`id`), KEY `idx_pinyin` (`pinyin`), KEY `idx_pinyin_abbr` (`pinyin_abbr`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
PHP实现
<?php
class PinyinFullTextSearch
{
private $pdo;
private $pinyin;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
$this->pinyin = new Pinyin();
}
/**
* 数据插入时同步拼音
*/
public function addProduct($name)
{
$pinyin = $this->pinyin->convert($name);
$abbr = $this->pinyin->abbr($name);
$stmt = $this->pdo->prepare(
"INSERT INTO products (name, pinyin, pinyin_abbr)
VALUES (?, ?, ?)"
);
$stmt->execute([
$name,
implode('', $pinyin),
$abbr
]);
return $this->pdo->lastInsertId();
}
/**
* 高效拼音搜索 - SQL查询
*/
public function search($keyword)
{
// 将关键词转为拼音
$pinyinKeyword = $this->pinyin->convert($keyword);
$abbrKeyword = $this->pinyin->abbr($keyword);
$sql = "SELECT * FROM products
WHERE
name LIKE :keyword OR
pinyin LIKE :pinyin OR
pinyin_abbr LIKE :abbr
ORDER BY
CASE
WHEN name LIKE :keyword_exact THEN 1
WHEN pinyin LIKE :pinyin_exact THEN 2
WHEN pinyin_abbr LIKE :abbr_exact THEN 3
ELSE 4
END
LIMIT 20";
$stmt = $this->pdo->prepare($sql);
$like = '%' . $keyword . '%';
$likePinyin = '%' . implode('', $pinyinKeyword) . '%';
$likeAbbr = '%' . $abbrKeyword . '%';
$stmt->execute([
'keyword' => $like,
'pinyin' => $likePinyin,
'abbr' => $likeAbbr,
'keyword_exact' => $keyword . '%',
'pinyin_exact' => implode('', $pinyinKeyword) . '%',
'abbr_exact' => $abbrKeyword . '%'
]);
return $stmt->fetchAll();
}
}
Redis缓存方案(高性能)
<?php
class PinyinRedisSearch
{
private $redis;
private $pinyin;
public function __construct(Redis $redis)
{
$this->redis = $redis;
$this->pinyin = new Pinyin();
}
/**
* 初始化拼音索引
*/
public function buildIndex($products)
{
foreach ($products as $product) {
$pinyin = $this->pinyin->convert($product['name']);
$abbr = $this->pinyin->abbr($product['name']);
// 建立索引
$this->redis->sAdd('pinyin:' . implode('', $pinyin), $product['id']);
$this->redis->sAdd('pinyin_abbr:' . $abbr, $product['id']);
$this->redis->sAdd('pinyin_space:' . implode(' ', $pinyin), $product['id']);
// 建立模糊索引(每个拼音前缀)
$fullPinyin = implode('', $pinyin);
for ($i = 1; $i <= strlen($fullPinyin); $i++) {
$prefix = substr($fullPinyin, 0, $i);
$this->redis->sAdd('pinyin_prefix:' . $prefix, $product['id']);
}
}
}
/**
* 快速搜索
*/
public function search($keyword)
{
if (preg_match('/[\x{4e00}-\x{9fa5}]/u', $keyword)) {
// 中文搜索
$ids = $this->redis->sInter('pinyin:' . $this->pinyin->convert($keyword));
} else {
// 拼音搜索
$ids = $this->redis->sMembers('pinyin_prefix:' . strtolower($keyword));
}
// 获取产品信息
$products = [];
foreach ($ids as $id) {
$products[] = $this->getProductById($id);
}
return $products;
}
}
前端+后端配合方案
PHP后端返回拼音数据
<?php
// 返回含有拼音的数据
public function getProductsWithPinyin()
{
$products = $this->getAllProducts();
foreach ($products as &$product) {
$pinyin = $this->pinyin->convert($product['name']);
$product['pinyin'] = implode('', $pinyin);
$product['pinyin_abbr'] = $this->pinyin->abbr($product['name']);
$product['pinyin_array'] = $pinyin; // 每个字对应的拼音
}
return json_encode($products);
}
前端JavaScript实现实时搜索
// search.js
class PinyinSearchBox {
constructor(data) {
this.data = data;
this.init();
}
init() {
this.searchInput.addEventListener('input', (e) => {
const keyword = e.target.value.toLowerCase();
this.filterData(keyword);
});
}
filterData(keyword) {
if (!keyword) {
this.renderAll();
return;
}
const results = this.data.filter(item => {
// 中文匹配
if (item.name.includes(keyword)) return true;
// 拼音匹配
if (item.pinyin.includes(keyword)) return true;
// 首字母匹配
if (item.pinyin_abbr.includes(keyword)) return true;
// 拼音首字母与汉字混合匹配
const keywordArray = keyword.split('');
let pinyinIndex = 0;
let nameIndex = 0;
while (nameIndex < item.name.length && pinyinIndex < keywordArray.length) {
// 检查是否为汉字
if (/[\u4e00-\u9fa5]/.test(keywordArray[pinyinIndex])) {
if (item.name[nameIndex] === keywordArray[pinyinIndex]) {
pinyinIndex++;
}
nameIndex++;
} else {
// 拼音匹配
const pinyinChar = item.pinyin_array[nameIndex] || '';
if (pinyinChar.startsWith(keywordArray[pinyinIndex])) {
pinyinIndex++;
nameIndex++;
} else {
return false;
}
}
}
return pinyinIndex === keywordArray.length;
});
this.renderResults(results);
}
}
Elasticsearch搜索方案(企业级)
<?php
// 使用Elasticsearch
use Elastic\Elasticsearch\ClientBuilder;
class ElasticPinyinSearch
{
private $client;
public function __construct()
{
$this->client = ClientBuilder::create()
->setHosts(['localhost:9200'])
->build();
}
/**
* 创建索引(带拼音分析器)
*/
public function createIndex()
{
$params = [
'index' => 'products',
'body' => [
'settings' => [
'analysis' => [
'analyzer' => [
'pinyin_analyzer' => [
'type' => 'custom',
'tokenizer' => 'standard',
'filter' => ['pinyin_filter']
]
],
'filter' => [
'pinyin_filter' => [
'type' => 'pinyin',
'keep_full_pinyin' => true,
'keep_first_letter' => true,
'keep_joined_full_pinyin' => true
]
]
]
],
'mappings' => [
'properties' => [
'name' => [
'type' => 'text',
'analyzer' => 'pinyin_analyzer',
'search_analyzer' => 'standard'
]
]
]
]
];
return $this->client->indices()->create($params);
}
/**
* 拼音搜索
*/
public function search($keyword)
{
$params = [
'index' => 'products',
'body' => [
'query' => [
'multi_match' => [
'query' => $keyword,
'fields' => ['name^3', 'name.pinyin']
]
],
'highlight' => [
'fields' => [
'name' => ['pre_tags' => ['<b>'], 'post_tags' => ['</b>']]
]
]
]
];
return $this->client->search($params);
}
}
建议
- 小项目:使用
overtrue/pinyin包,简单易用 - 中型项目:数据库添加拼音字段 + 索引
- 大型项目:Elasticsearch + IK分析器 + 拼音插件
- 高并发:Redis缓存 + 拼音索引
需要哪种具体方案的完整实现,或者有特定的使用场景,可以详细说说。