Read this documentation in Russian.
bermudaphp/stringy is a comprehensive PHP string manipulation library with full Unicode support. It provides both immutable and mutable string classes with a consistent API, making string operations more reliable and convenient compared to native PHP functions.
composer require bermudaphp/stringy
The library provides two main string classes:
- Str: Immutable string class. Operations return new string instances without modifying the original.
- StrMutable: Mutable string class. Operations modify the string in place and return the same instance.
Both implement the StringInterface contract, providing a consistent API for string manipulation.
The immutable string class returns a new instance for every operation that would change the string.
use Bermuda\Stdlib\Str;
// Create from string
$str = Str::from('Hello World');
// Create with specific encoding
$russianStr = Str::from('Привет мир', 'UTF-8');
// Create through constructor
$str = new Str('Hello World');
// alternative
$str = Stringy::of('Hello World');
The createLazy()
static method allows you to create lazily initialized string objects that are only constructed when actually accessed. This can significantly improve performance by delaying expensive operations until they're truly needed.
// Create a lazy-initialized string object
$lazy = \Bermuda\Stdlib\Str::createLazy(static function (\Bermuda\Stdlib\Str $str) {
$str->__construct('construct call');
});
// No initialization occurs until the object is accessed
echo $lazy->value; // Triggers initialization: "construct call"
Unlike traditional objects that are initialized immediately, lazy ghost objects:
Create a minimal placeholder object initially.
Only execute your initializer function when a property or method is accessed.
Initialize the object just-in-time with the values you specify.
Lazy initialization is particularly valuable when:
Resource-heavy initialization: Loading data from files, databases, or APIs.
Conditional usage: When objects may not be used in all code paths.
Performance optimization: Delaying expensive operations until absolutely necessary.
Memory management: Reducing memory usage by not initializing unused objects.
// Creating multiple lazy string objects for a report
$reportFields = [
'title' => Str::createLazy(function($str) use ($reportId) {
$data = $database->fetchReportTitle($reportId);
$str->__construct($data);
}),
'summary' => Str::createLazy(function($str) use ($reportId) {
$data = $database->fetchReportSummary($reportId);
$str->__construct($data);
}),
'content' => Str::createLazy(function($str) use ($reportId) {
// This potentially large content is only loaded if actually displayed
$data = $database->fetchReportContent($reportId);
$str->__construct($data);
})
];
// Only the title and summary are initialized - content remains lazy
echo $reportFields['title']->toUpperCase();
echo $reportFields['summary']->truncate(100);
// If this condition is false, content is never loaded from the database
if ($showFullReport) {
echo $reportFields['content'];
}
$str = Str::from('Hello');
// Get the underlying string value
echo $str->value; // "Hello"
// Get the current encoding
echo $str->encoding; // "UTF-8" (default)
// Check if string contains multibyte characters
var_dump($str->isMultibyte); // bool(false)
$russianStr = Str::from('Привет');
var_dump($russianStr->isMultibyte); // bool(true)
$str = Str::from('Hello World');
// Convert to string
echo $str->toString(); // "Hello World"
echo $str; // "Hello World" (uses __toString())
// Create a copy
$copy = $str->copy(); // Creates new Str instance with same value
$copy === $str; // false
// Encode to different character set
$latin1 = $str->encode('ISO-8859-1');
// Get byte count (different from character count for multibyte strings)
echo $str->getBytes(); // 11
// Get string length in characters
echo $str->length(); // 11
echo count($str); // 11 (using Countable interface)
// Check if string is empty
var_dump($str->isEmpty()); // bool(false)
var_dump(Str::from('')->isEmpty()); // bool(true)
// Check if string is blank (empty or only whitespace)
var_dump(Str::from(' ')->isBlank()); // bool(true)
$str = Str::from('Hello World');
// Get character at position
echo $str->charAt(0); // "H"
echo $str->charAt(6); // "W"
echo $str->charAt(-1); // "d" (negative indices count from end)
// Get character as a new string object
$firstChar = $str->at(0); // Returns Str('H')
$lastChar = $str->at(-1); // Returns Str('d')
// Get first and last characters
$first = $str->first(); // Returns Str('H')
$last = $str->last(); // Returns Str('d')
// Check if position exists
var_dump($str->has(5)); // bool(true)
var_dump($str->has(20)); // bool(false)
// Get index bounds
echo $str->firstIndex(); // 0
echo $str->lastIndex(); // 10
// Array access (for reading only in Str)
echo $str[0]; // "H"
echo $str[-1]; // "d"
$str = Str::from('Hello World');
// Extract substring
echo $str->substring(0, 5); // "Hello"
echo $str->substring(6); // "World" (to the end)
echo $str->substring(-5); // "World" (from 5th last character to end)
// Get start/end of string
echo $str->start(5); // "Hello"
echo $str->end(5); // "World"
// Remove from start/end
echo $str->removeStart(6); // "World"
echo $str->removeEnd(6); // "Hello"
// Get substring between delimiters
echo $str->between('H', 'o'); // "ell"
// Get substring before/after
echo $str->before('World'); // "Hello "
echo $str->after('Hello'); // " World"
// Include delimiter in before/after
echo $str->before('World', true); // "Hello W"
echo $str->after('Hello', true); // "Hello World"
// Split into two parts
list($first, $second) = $str->split(' ');
echo $first; // "Hello"
echo $second; // "World"
// Split by delimiter
$parts = $str->explode(' '); // Returns array of Str objects
$parts = $str->explode(' ', PHP_INT_MAX, true); // Returns array of strings
$str = Str::from('Hello World');
// Compare with another string
var_dump($str->equals('Hello World')); // bool(true)
var_dump($str->equals('hello world')); // bool(false)
var_dump($str->equals('hello world', false)); // bool(true) - case insensitive
// Compare with any of multiple strings
var_dump($str->equalsAny(['Hello', 'World', 'Hello World'])); // bool(true)
// Check if starts with
var_dump($str->startsWith('Hello')); // bool(true)
var_dump($str->startsWith(['Hi', 'Hello'])); // bool(true)
var_dump($str->startsWith('hello', false)); // bool(true) - case insensitive
// Check if ends with
var_dump($str->endsWith('World')); // bool(true)
var_dump($str->endsWith(['Earth', 'World'])); // bool(true)
// Check if contains
var_dump($str->contains('lo Wo')); // bool(true)
var_dump($str->contains(['lo', 'Wo'])); // bool(true)
// Check if contains all
var_dump($str->containsAll(['Hello', 'World'])); // bool(true)
$str = Str::from('Hello World, Hello Earth');
// Find position of substring
echo $str->indexOf('Hello'); // 0
echo $str->indexOf('Hello', 1); // 13 (starting from position 1)
echo $str->indexOf('hello', 0, false); // 0 (case insensitive)
var_dump($str->indexOf('Bye')); // NULL (not found)
// Find last position of substring
echo $str->lastIndexOf('Hello'); // 13
// Count occurrences
echo $str->countSubstr('Hello'); // 2
echo $str->countSubstr('hello', false); // 2 (case insensitive)
$str = Str::from('hello world');
// Convert to uppercase/lowercase
echo $str->toUpperCase(); // "HELLO WORLD"
echo $str->toLowerCase(); // "hello world"
// Capitalize first character
echo $str->capitalize(); // "Hello world"
// Uncapitalize first character
echo Str::from('Hello World')->uncapitalize(); // "hello World"
// Capitalize each word
echo $str->capitalizeWords(); // "Hello World"
// Swap case of each character
echo Str::from('Hello')->swapCase(); // "hELLO"
// Title case (smarter capitalization)
echo $str->titleize(); // "Hello World"
echo $str->titleize(['of', 'the']); // "Hello World" (with ignored words)
$str = Str::from('hello_world-example');
// Convert to different formats
echo $str->toCamelCase(); // "helloWorldExample"
echo $str->toPascalCase(); // "HelloWorldExample"
echo $str->toSnakeCase(); // "hello_world_example"
echo $str->toKebabCase(); // "hello-world-example"
// Custom delimiter
echo $str->delimit('.'); // "hello.world.example"
$str = Str::from(' Hello World ');
// Trim whitespace
echo $str->trim(); // "Hello World"
echo $str->trimStart(); // "Hello World "
echo $str->trimEnd(); // " Hello World"
// Custom characters to trim
echo Str::from('__Hello__')->trim('_'); // "Hello"
// Remove all whitespace
echo $str->stripWhitespace(); // "HelloWorld"
// Collapse multiple whitespace to single space
echo Str::from('Hello World')->collapseWhitespace(); // "Hello World"
$str = Str::from('Hello World');
// Insert at position
echo $str->insert(' Dear', 5); // "Hello Dear World"
// Pad string
echo $str->pad('*', 15); // "**Hello World**"
echo $str->padStart('-', 15); // "----Hello World"
echo $str->padEnd('=', 15); // "Hello World===="
// Wrap with character
echo $str->wrap('"'); // "\"Hello World\""
// Check if wrapped
var_dump(Str::from('"Hello"')->isWrapped('"')); // bool(true)
// Add to start/end
echo $str->prepend('Dear '); // "Dear Hello World"
echo $str->append('!'); // "Hello World!"
// Add prefix/suffix if not exists
echo $str->ensurePrefix('Hello '); // "Hello World" (unchanged)
echo $str->ensureSuffix('!'); // "Hello World!"
// Remove prefix/suffix
echo Str::from('HelloWorld')->removeSuffix('World'); // "Hello"
echo Str::from('HelloWorld')->removePrefix('Hello'); // "World"
$str = Str::from('Hello World');
// Replace text
echo $str->replace('World', 'Earth'); // "Hello Earth"
echo $str->replace(['Hello', 'World'], ['Hi', 'Earth']); // "Hi Earth"
echo $str->replace('world', 'Earth', false); // "Hello Earth" (case insensitive)
// Replace first/last occurrence
echo Str::from('Hello Hello')->replaceFirst('Hello', 'Hi'); // "Hi Hello"
echo Str::from('Hello Hello')->replaceLast('Hello', 'Hi'); // "Hello Hi"
// Replace with pattern
echo $str->replaceBy('/[aeiou]/i', '*'); // "H*ll* W*rld"
// Replace with callback
$result = $str->replaceCallback('/[A-Z]/u', function($match) {
return '_' . strtolower($match[0]);
}); // "_hello _world"
$str = Str::from('Hello World');
// Reverse
echo $str->reverse(); // "dlroW olleH"
// Shuffle characters
echo $str->shuffle(); // Random order, e.g. "ldWroHl eol"
// Repeat
echo Str::from('Hi ')->repeat(3); // "Hi Hi Hi "
// Truncate
echo Str::from('This is a long sentence')->truncate(10); // "This is..."
echo Str::from('This is a long sentence')->truncate(10, '---'); // "This is---"
echo Str::from('This is a long sentence')->truncate(10, '...', true); // "This..."
// Transform with callback
echo $str->transform(function($s) {
return strtoupper($s) . '!';
}); // "HELLO WORLD!"
// Tabs and spaces
echo Str::from("Hello\tWorld")->tabsToSpaces(2); // "Hello World"
echo Str::from("Hello World")->spacesToTabs(2); // "Hello\tWorld"
// Format with sprintf
echo Str::from('Hello, %s!')->format('John'); // "Hello, John!"
$str = Str::from('123');
// Check types
var_dump($str->isNumeric()); // bool(true)
var_dump($str->isAlpha()); // bool(false)
var_dump($str->isAlphanumeric()); // bool(true)
var_dump($str->isHex()); // bool(true)
var_dump($str->isUpperCase()); // bool(false)
var_dump($str->isLowerCase()); // bool(false)
var_dump($str->hasUpperCase()); // bool(false)
var_dump($str->hasLowerCase()); // bool(false)
var_dump($str->hasDigits()); // bool(true)
var_dump($str->hasSymbols()); // bool(false)
// Convert to types
echo $str->toNumber(); // int(123)
var_dump(Str::from('true')->toBoolean()); // bool(true)
var_dump(Str::from('true')->isBoolean()); // bool(true)
// JSON operations
var_dump(Str::from('{"a":1}')->isJson()); // bool(true)
echo Str::from('Hello')->toJson(); // "\"Hello\""
// Other type checks
var_dump(Str::from('a:1:{s:1:"a";i:1;}')->isSerialized()); // bool(true)
var_dump(Str::from('SGVsbG8=')->isBase64()); // bool(true)
var_dump(Str::from('2023-05-17')->isDate()); // bool(true)
// Convert to date
$date = Str::from('2023-05-17')->toDate();
$str = Str::from("Hello\nWorld\nExample");
// Split into lines
$lines = $str->lines(); // Array of Str objects, one per line
// Split into words
$words = Str::from('Hello World Example')->words(); // ["Hello", "World", "Example"]
// Convert to array of characters
$chars = $str->toArray(); // ["H", "e", "l", "l", "o", ... ]
$str = Str::from('Hello 123 World');
// Match pattern
$found = $str->match('/\d+/', $matches);
var_dump($found); // bool(true)
echo $matches[0]; // "123"
// Match all occurrences
$found = $str->matchAll('/\w+/', $matches);
var_dump($matches[0]); // Array with all words
$str = Str::from('Hello World');
// Hash the string
echo $str->hash(); // SHA-256 hash
echo $str->hash('md5'); // MD5 hash
// ASCII conversion
echo Str::from('Café')->toAscii(); // "Cafe"
// Output
$str->print(); // Outputs "Hello World"
// Iterate through characters
$str->each(function($char, $index) {
echo "$index: $char\n";
return true; // Continue iteration
});
$str = Str::from('Hello');
// Get iterator
$iterator = $str->getIterator();
// Use in foreach
foreach ($iterator as $index => $char) {
echo "$index: $char\n";
}
The mutable string class modifies the string in place and returns the same instance for method chaining.
use Bermuda\Stdlib\StrMutable;
// Create from string
$str = StrMutable::create('Hello World');
// Create with specific encoding
$russianStr = StrMutable::create('Привет мир', 'UTF-8');
// Create through constructor
$str = new StrMutable('Hello World');
// alternative
$str = Stringy::mutable('Hello World');
// Set string value directly
$str = StrMutable::create('Hello');
$str->setString('New Value');
Most methods in StrMutable have the same API as Str, but modify the string in place:
$str = StrMutable::create('Hello World');
// Chain operations
$str->toUpperCase()
->trim()
->replace('WORLD', 'EARTH');
echo $str; // "HELLO EARTH"
// Substring modifies in place
$str->substring(0, 5);
echo $str; // "HELLO"
$str = StrMutable::create('Hello');
// Read
echo $str[1]; // "e"
// Write
$str[0] = 'J';
echo $str; // "Jello"
// Remove character
unset($str[4]);
echo $str; // "Jell"
The StringIterator class allows character-by-character iteration with various navigation methods.
use Bermuda\Stdlib\StringIterator;
// Create directly
$iterator = new StringIterator('Hello');
// Create through string object
$str = Str::from('World');
$iterator = $str->getIterator();
// Basic iteration
while ($iterator->valid()) {
echo $iterator->current();
$iterator->next();
}
// Outputs: "World"
// Get the string
echo $iterator->__toString(); // "World"
// Create new iterator with different string
$newIterator = $iterator->withString('Hello');
$iterator = new StringIterator('Hello World');
// Get current state
echo $iterator->current(); // "H" (initial position is 0)
echo $iterator->key(); // 0 (current position)
// Move forward/backward
$iterator->next();
echo $iterator->current(); // "e"
$iterator->forward(2); // Move 2 steps forward
echo $iterator->current(); // "l"
$iterator->backward(); // Move 1 step back
echo $iterator->current(); // "e"
// Jump to position
$iterator->moveTo(6);
echo $iterator->current(); // "W"
// Reset position
$iterator->rewind();
echo $iterator->key(); // 0
// Check position
var_dump($iterator->isStart()); // bool(true)
var_dump($iterator->isEnd()); // bool(false)
$iterator->moveTo($iterator->lastIndex());
var_dump($iterator->isEnd()); // bool(false)
$iterator->next();
var_dump($iterator->isEnd()); // bool(true)
var_dump($iterator->valid()); // bool(false)
$iterator = new StringIterator('Hello World');
// Read next characters
$iterator->moveTo(6);
echo $iterator->readNext(5); // "World"
// Read to the end (when length is null)
$iterator->moveTo(6);
echo $iterator->readNext(); // "World"
The Stringy class provides static utility methods for string manipulation.
use Bermuda\Stdlib\Stringy;
// Convert string format
echo Stringy::delimit('HelloWorld', '-'); // "hello-world"
echo Stringy::delimit('hello_world-example', '.'); // "hello.world.example"
// Trim whitespace
echo Stringy::trim(' Hello '); // "Hello"
echo Stringy::trimStart(' Hello '); // "Hello "
echo Stringy::trimEnd(' Hello '); // " Hello"
// With custom characters
echo Stringy::trim('__Hello__', '_'); // "Hello"
// Multibyte support
echo Stringy::trim(' Привет '); // "Привет"
The ClsHelper class provides utilities for working with class names and namespaces.
use Bermuda\Stdlib\ClsHelper;
$className = 'Bermuda\\Stringy\\StrMutable';
// Get namespace part
echo ClsHelper::namespace($className); // "Bermuda\\Stringy"
// Get basename (class without namespace)
echo ClsHelper::basename($className); // "StrMutable"
// Split into namespace and class name
$parts = ClsHelper::split($className);
// Returns: [0 => 'Bermuda\\Stringy', 1 => 'StrMutable']
// Validate class names
var_dump(ClsHelper::isValidName('MyClass')); // bool(true)
var_dump(ClsHelper::isValidName('Vendor\\MyClass')); // bool(true)
var_dump(ClsHelper::isValidName('Vendor\\MyClass', false)); // bool(false) - no namespace allowed
var_dump(ClsHelper::isValidName('0InvalidClass')); // bool(false)
All methods in Bermuda\Stringy properly handle multibyte strings, ensuring correct character handling for non-Latin alphabets and special characters:
// Create with specific encoding
$russian = Str::from('Привет, мир!', 'UTF-8');
$chinese = Str::from('你好,世界!', 'UTF-8');
$arabic = Str::from('مرحبا بالعالم!', 'UTF-8');
// Character count vs byte count
echo $russian->length(); // 12 (character count)
echo $russian->getBytes(); // More than 12 (byte count)
// Character access
echo $russian->charAt(0); // "П"
echo $chinese->charAt(0); // "你"
// Substring extraction
echo $russian->substring(0, 6); // "Привет"
echo $chinese->substring(0, 2); // "你好"
// Case conversion (where applicable)
echo $russian->toUpperCase(); // "ПРИВЕТ, МИР!"
echo $russian->toLowerCase(); // "привет, мир!"
// Reverse (correctly handles multibyte)
echo $russian->reverse(); // "!рим ,тевирП"
// Replace
echo $russian->replace('мир', 'свет'); // "Привет, свет!"
// Trim
echo Str::from(' Привет ')->trim(); // "Привет"
// Character iterations
$iterator = $russian->getIterator();
foreach ($iterator as $char) {
echo $char . ' '; // "П р и в е т , м и р !"
}
$mutable = StrMutable::create('Привет');
echo $mutable[0]; // "П"
$mutable[0] = 'К';
echo $mutable; // "Кривет"
unset($mutable[5]);
echo $mutable; // "Криве"
// Japanese
$japanese = Str::from('こんにちは世界', 'UTF-8');
echo $japanese->length(); // 7 characters
echo $japanese->substring(0, 5); // "こんにちは"
// Thai (with combining diacritical marks)
$thai = Str::from('สวัสดีโลก', 'UTF-8');
echo $thai->length(); // Correctly counts Thai characters with marks
// Emoji
$emoji = Str::from('Hello 👋 World 🌍', 'UTF-8');
echo $emoji->length(); // Correctly counts emoji characters
echo $emoji->substring(6, 1); // "👋"
// Mixed script text
$mixed = Str::from('Hello Привет 你好 مرحبا', 'UTF-8');
foreach ($mixed->words() as $word) {
echo $word . "\n"; // Correctly splits words across scripts
}
This library is licensed under the MIT License. See the LICENSE file for details.
Contributions are welcome! Please create a pull request on the GitHub repository.