Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Phpanalysis Laravel Package

lmz/phpanalysis

A PHP Chinese word segmentation library based on PHPAnalysis. It splits Chinese text into words using dictionary rules, supports UTF-8/GBK input, and is useful for keyword extraction, search indexing, and basic text analysis in PHP projects.

View on GitHub
Deep Wiki
Context7

Getting Started

lmz/phpanalysis is a Composer wrapper around the classic single-class PhpAnalysis Chinese word segmentation library (中文分词). It is not an NLP toolkit — it does dictionary-based segmentation (forward/backward maximum matching), simplified/traditional conversion, and a rough keyword extractor. Treat it as "one class + one dictionary file."

Minimal setup:

composer require lmz/phpanalysis
find vendor/lmz/phpanalysis -maxdepth 2 -type f | head -50

You are looking for two things and nothing else matters: the class file (legacy PhpAnalysis or a namespaced variant under src/) and the dictionary (dict/, dic.txt, dict.txt). Confirm what actually autoloads before writing code:

require 'vendor/autoload.php';
var_dump(class_exists('PhpAnalysis'), class_exists('Lmz\PhpAnalysis\PhpAnalysis'));

First use case — segmenting a product title to build search terms:

$pa = new PhpAnalysis('utf-8', 'utf-8', true); // last arg = extract keywords
$pa->LoadDict();
$pa->SetSource('小米手机官方旗舰店正品保障');
$pa->StartAnalysis();

$words    = $pa->GetFinallyResult();    // ['小米','手机','官方','旗舰店',...]
$keywords = $pa->GetFinallyKeywords(5); // top-N by the library's internal weighting

If the snippet fails on method names, open the class file and read SetSource / StartAnalysis / GetFinallyResult — the whole library is ~a few hundred lines and reading it once is faster than fighting it.

Implementation Patterns

Load the dictionary once, segment many. Dictionary parsing is the dominant cost. Put the instance behind a singleton, not a new per call.

// app/Support/ChineseSegmenter.php
class ChineseSegmenter
{
    private PhpAnalysis $pa;

    public function __construct()
    {
        $this->pa = new PhpAnalysis('utf-8', 'utf-8', true);
        $this->pa->LoadDict();
    }

    public function segment(string $text): array
    {
        $this->pa->SetSource($text);
        $this->pa->StartAnalysis();
        return $this->pa->GetFinallyResult();
    }
}
// AppServiceProvider: $this->app->singleton(ChineseSegmenter::class);

Cache the parsed dictionary. If you process batches (queue jobs, re-indexing), serializing the loaded dictionary once to storage/framework/cache and rehydrating it beats re-parsing the text file per worker.

Extend the dictionary, don't post-process. Brand names, SKUs, and internal product codes fragment badly out of the box. Append domain terms to the shipped dictionary (or via the library's add-word hook if the version exposes one) rather than gluing pieces back together with regex afterwards.

Stopwords and punctuation are your job. Segment output routinely includes punctuation and high-frequency noise (的, 了, 和). Filter with a config/stopwords.php array after segmentation; keep the list in your app, not in vendor/.

Search integration. Segment the query, then either build LIKE '%term%' clauses per term, or (better) push the segmented terms into Meilisearch/TNTSearch/Elasticsearch with a proper CJK analyzer. Segmentation output is also a cheap source of tags, related-article links, and sitemap keyword pages.

Bulk pipelines. For indexing jobs, segment in chunks and unset() the instance between chunks — long-lived processes accumulate result state across SetSource calls in some builds.

Gotchas and Tips

  • Encoding is silent-failure mode. Source text must match the encoding passed to the constructor. One GBK string in a UTF-8 pipeline gives you garbage segments with no warning. Normalize with mb_convert_encoding($s, 'UTF-8', 'auto') at the boundary.
  • Dictionary path resolution is the #1 breakage. Legacy builds resolve the dict relative to getcwd() or a hardcoded relative path. Under queue workers, Octane, or a differently-rooted CLI this fails or loads nothing. Verify the loaded entry count after LoadDict() and patch the path to use __DIR__ if needed.
  • Memory footprint. A full dictionary expands into PHP arrays worth tens of MB. Check memory_get_usage() before deploying to a small php-fpm pool, and never load it in a request that doesn't segment anything.
  • Instance reuse is not guaranteed clean. GetFinallyResult() may return accumulated results across calls in some versions. Re-instantiate for safety, or explicitly clear result state, and write a test that segments two different strings in sequence.
  • GetFinallyKeywords() is a heuristic, not TF-IDF. Its ordering is roughly frequency-and-length based. Don't use it to rank search results or compute relatedness scores — segment first, then score with your own weights.
  • Turn off traditional/simplified conversion if you don't need it; it's pure overhead on every call.
  • Unmaintained and effectively unused (2 stars, 0 dependents). Pin an exact version in composer.json, and consider vendoring the single class into app/Support/ behind your own interface so you can swap to fukuball/jieba-php, the scws extension, or an Elasticsearch IK analyzer without touching call sites.
  • Long-lived workers (Octane/Swoole): the singleton-with-dictionary pattern is actually an advantage — the dict loads once per worker. But confirm the library holds no per-request global state, and add a health check that segmentation still returns non-empty output after a few thousand requests.
  • Debugging: dump the intermediate result array before GetFinallyResult() joins it, and diff against a known-good segmentation of the same sentence. If every character comes back as its own token, the dictionary didn't load — not an algorithm problem.
  • Extension points are thin. Subclassing PhpAnalysis to override matching or the keyword weighting is the only real hook; upstream a fork if you need behavior changes, and add tests the package doesn't ship.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console