soloterm / grapheme
A PHP package to measure the width of unicode strings rendered to a terminal.
Fund package maintenance!
aarondfrancis
aaronfrancis.com/backstage
Requires
- php: ^8.2
- symfony/polyfill-intl-normalizer: 1.x-dev
Requires (Dev)
- phpunit/phpunit: ^10.5|^11
Suggests
- ext-intl: For best performance
README
A highly optimized PHP library for calculating the display width of Unicode graphemes in terminal environments. Accurately determine how many columns a character will occupy in the terminal, including complex emoji, combining marks, and more.
This library was built to support Solo, your all-in-one Laravel command to tame local development.
Why Use This Library?
Building CLI applications can be challenging when it comes to handling modern Unicode text:
- Emoji and CJK characters take up 2 cells in most terminals
- Zero-width characters (joiners, marks, etc.) don't affect layout but can cause width calculation errors
- Complex text like emoji with skin tones or flags require special handling
- PHP's built-in functions don't fully address these edge cases
This library solves these problems by providing an accurate, performant, and thoroughly tested way to determine the display width of any character or grapheme cluster.
Installation
composer require soloterm/grapheme
Usage
use SoloTerm\Grapheme\Grapheme; // Basic characters (width: 1) Grapheme::wcwidth('a'); // Returns: 1 Grapheme::wcwidth('ะฏ'); // Returns: 1 // East Asian characters (width: 2) Grapheme::wcwidth('ๆ'); // Returns: 2 Grapheme::wcwidth('ใ'); // Returns: 2 // Emoji (width: 2) Grapheme::wcwidth('๐'); // Returns: 2 Grapheme::wcwidth('๐'); // Returns: 2 // Complex emoji with modifiers (width: 2) Grapheme::wcwidth('๐๐ป'); // Returns: 2 Grapheme::wcwidth('๐จโ๐ฉโ๐งโ๐ฆ'); // Returns: 2 // Zero-width characters (width: 0) Grapheme::wcwidth("\u{200B}"); // Returns: 0 (Zero-width space) // Characters with combining marks (width: 1) Grapheme::wcwidth('รฉ'); // Returns: 1 Grapheme::wcwidth("e\u{0301}"); // Returns: 1 (e + combining acute) // Special cases Grapheme::wcwidth("โ \u{FE0E}"); // Returns: 1 (Warning sign in text presentation) Grapheme::wcwidth("โ \u{FE0F}"); // Returns: 2 (Warning sign in emoji presentation)
Features
- Highly optimized for performance with early-return paths and smart caching
- Comprehensive Unicode support including:
- CJK (Chinese, Japanese, Korean) characters
- Emoji (including skin tone modifiers, gender modifiers, flags)
- Zero-width characters and control codes
- Combining marks and accents
- Regional indicators and flags
- Variation selectors
- Carefully tested against a wide range of Unicode characters
- Minimal dependencies - only requires PHP 8.2+ and an optional intl extension
- Compatible with most terminal environments
Terminal Compatibility
This library aims to match the behavior of wcwidth()
in modern terminal emulators.
Requirements
- PHP 8.2 or higher
- The
symfony/polyfill-intl-normalizer
package is included as a dependency - The
ext-intl
extension is recommended for best performance
Under the Hood
The library uses a series of optimized patterns and checks to accurately determine character width:
- Fast paths for ASCII and zero-width characters
- Special handling for complex scripts like Devanagari
- Smart detection of emoji and variation selectors
- Proper handling of zero-width joiners (ZWJ) and other invisible characters
- Caching of results for improved performance
Testing
composer test
The test suite includes over 150 different test cases covering many possible Unicode scenarios. Please feel free to add more.
Contributing
Contributions are welcome! Please feel free to submit a pull request.
License
The MIT License (MIT).
Support
This is free! If you want to support me:
- Sponsor my open source work: aaronfrancis.com/backstage
- Check out my courses:
- Help spread the word about things I make
Credits
Solo was developed by Aaron Francis. If you like it, please let me know!
- Twitter: https://twitter.com/aarondfrancis
- Website: https://aaronfrancis.com
- YouTube: https://youtube.com/@aarondfrancis
- GitHub: https://github.com/aarondfrancis/solo