Skip to content
PHP scalars objects to enhance data operations.

String Object ​

StringObject wraps a string together with the encoding used to read it. Its methods cover the text operations that code uses most often: trimming, searching, case conversion, padding, slicing, and conversion to numbers. Many PHP string functions are byte-oriented and take their arguments in different orders. A chain of StringObject calls reads as a sequence of text operations, and its length and position methods follow the encoding.

A typical case is normalizing user input before it is stored or shown, such as a title that comes from a rich-text field and needs tags removed, spaces collapsed, and a length limit applied.

Method Summary ​

Methods that return New object leave the original unchanged. Methods that return a native value, such as bool, int, or string, do not create a new StringObject. The methods below are either defined on the class or forwarded to Windwalker\Utilities\Str. Methods with a link have a section below that explains them.

MethodReturnsDescription
str()New objectHelper; creates a StringObject with UTF-8 by default.
create()New objectStatic factory.
wrap()ObjectStatic; returns the same instance or wraps a value.
getString()stringReturns the native string.
withString()New objectCopy with a different string.
getEncoding()stringReturns the encoding.
withEncoding()New objectCopy with a different encoding.
length()intCharacter count in its encoding.
count()intSame as length().
isNull()booltrue only for an empty string.
notNull()booltrue for a non-empty string.
indexOf()intPosition of the first match.
indexOfLast()intPosition of the last match.
substrCount()intNumber of occurrences.
compare()intCompares with another string.
contains()boolWhether the string contains a substring.
startsWith()boolWhether the string starts with a substring.
endsWith()boolWhether the string ends with a substring.
hasLowerCase()boolWhether it contains a lowercase letter.
hasUpperCase()boolWhether it contains an uppercase letter.
isLowerCase()boolWhether it is entirely lowercase.
isUpperCase()boolWhether it is entirely uppercase.
isPlural()boolWhether the word is plural.
isSingular()boolWhether the word is singular.
toPlural()New objectConverts the word to plural.
toSingular()New objectConverts the word to singular.
explode()ArrayObjectSplits by a delimiter.
chop()ArrayObjectSplits into chunks of a length.
toInteger()intConverts to an integer.
toFloat()floatConverts to a float.
jsonDecode()CollectionDecodes JSON; needs windwalker/data.
parseToCollection()CollectionParses a format into a Collection.
toDOMDocument()DOMDocumentParses as an XML document.
toHTMLDocument()DOMDocumentParses as an HTML document.
toSimpleXML()SimpleXMLElementParses as a SimpleXML element.
toUpperCase()New objectConverts to uppercase.
toLowerCase()New objectConverts to lowercase.
upperCaseFirst()New objectUppercases the first character.
lowerCaseFirst()New objectLowercases the first character.
upperCaseWords()New objectUppercases the first letter of each word.
toggleCase()New objectSwaps the case of each letter.
trim()New objectTrims both ends.
trimLeft()New objectTrims the start.
trimRight()New objectTrims the end.
collapseWhitespaces()New objectCollapses repeated whitespace.
padStart()New objectPads the start to a length.
padEnd()New objectPads the end to a length.
pad()New objectPads both ends to a length.
ensureStart()New objectAdds a prefix if it is missing.
ensureEnd()New objectAdds a suffix if it is missing.
removeStart()New objectRemoves a prefix if present.
removeEnd()New objectRemoves a suffix if present.
surrounds()New objectWraps with a substring on both ends.
intersect()New objectReturns the common prefix of two strings.
intersectStart()New objectReturns the common prefix.
intersectEnd()New objectReturns the common suffix.
replace()New objectReplaces plain-text search strings.
replaceCallback()New objectReplaces regex matches through a callback.
substrReplace()New objectReplaces a portion by position.
reverse()New objectReverses the characters.
insert()New objectInserts a string at a position.
removeChar()New objectRemoves characters at a position.
slice()New objectReturns a slice by start and end.
substring()New objectReturns a substring by start and end.
between()New objectReturns the text between two markers.
truncate()New objectShortens to a length with a suffix.
first()New objectReturns the first characters.
last()New objectReturns the last characters.
getChar()New objectReturns the character at a position.
stripHtmlTags()New objectRemoves HTML tags, optionally keeping some.
apply()New objectPasses the string to a callback.
append()New objectReturns a copy with a string appended.
prepend()New objectReturns a copy with a string prepended.
map()New objectMaps each character and keeps the string.
filter()New objectKeeps characters that match a callback.
reject()New objectDrops characters that match a callback.

Writes to $s[0] or unset($s[0]) change the object in place. The table omits deprecated aliases, such as ensureLeft() and padLeft().

Creating a StringObject ​

Use the str() helper. It uses UTF-8 by default. Pass an encoding as the second argument when the text uses another charset:

php
use function Windwalker\str;

$a = str('Hello');
$b = str('Hello', 'US-ASCII');

StringObject::wrap() returns the same instance when the given value is already a StringObject. Otherwise it converts the value to a string first, so it accepts scalars and Stringable objects.

Showcase ​

The following example turns a rich-text field into a short, readable title:

php
use function Windwalker\str;

$html = '<p>  windwalker   scalars  is a <b>chainable</b> toolkit.</p>';

$title = str($html)
    ->stripHtmlTags()
    ->trim()
    ->collapseWhitespaces()
    ->upperCaseWords();

echo $title;
// Windwalker Scalars Is A Chainable Toolkit.

echo $title->truncate(20, '...');
// Windwalker Scalars I...

Each step returns a new StringObject, so $title can be reused for the full title and for the truncated version without repeating the cleanup.

Inspecting a String ​

Methods such as length(), contains(), startsWith(), and endsWith() return a native value rather than a new object:

php
$s = str('Windwalker Scalars');

$s->length();                   // 18
$s->startsWith('Wind');         // true
$s->contains('SCALARS', false); // true, case-insensitive
$s->indexOf('Scalars');         // 11
$s->substrCount('a');           // 3

compare() returns a negative number, zero, or a positive number, like strcmp(). The case checks inspect the letters of the string:

php
str('abc')->compare('abd');  // -1
str('abc')->isLowerCase();   // true
str('abc')->hasUpperCase();  // false

isNull() and notNull() check for an empty string. A value of '0' is not null:

php
str('')->isNull();   // true
str('0')->isNull();  // false

Trimming and Whitespace ​

trim() removes whitespace from both ends. trimLeft() and trimRight() remove it from one end only. collapseWhitespaces() replaces each run of whitespace inside the string with a single space:

php
$text = str("  Hello   World \n");

$text->trim();                         // 'Hello   World'
$text->trimLeft();                     // 'Hello   World \n'
$text->trim()->collapseWhitespaces();  // 'Hello World'

Case Conversion ​

Case methods change the letters of a string. upperCaseWords() capitalizes each word, and toggleCase() swaps the case of every letter:

php
str('hello')->upperCaseFirst();          // 'Hello'
str('Hello')->lowerCaseFirst();          // 'hello'
str('windwalker scalars')->upperCaseWords(); // 'Windwalker Scalars'
str('aB')->toggleCase();                 // 'Ab'
str('Mixed Case')->toLowerCase();        // 'mixed case'

Prefix and Suffix ​

ensureStart() and ensureEnd() add a prefix or suffix only when it is missing. removeStart() and removeEnd() do the opposite. surrounds() wraps the string on both sides:

php
str('path')->ensureStart('/');  // '/path'
str('path')->ensureEnd('/');    // 'path/'
str('/path/')->removeStart('/')->removeEnd('/'); // 'path'
str('x')->surrounds('"');       // '"x"'

TIP

Use ensureStart() and removeEnd() for path and URL fragments. They check the prefix or suffix, so they do not change a string that already has it.

The intersect family returns the text that two strings share at one end. Use intersectStart() for a common prefix and intersectEnd() for a common suffix:

php
str('foobar')->intersectStart('fooz'); // 'foo'
str('foobar')->intersectEnd('zbar');   // 'bar'
str('foobar')->intersect('foobaz');    // 'fooba'

Padding ​

Padding adds characters until the string reaches a given length. The padStart() method adds them at the start, padEnd() at the end, and pad() on both sides:

php
str('7')->padStart(3, '0');  // '007'
str('ab')->padEnd(4, '-');   // 'ab--'
str('ab')->pad(6, '-');      // '--ab--'

Replacing Text ​

replace() swaps plain-text substrings. Pass arrays to replace several search strings with one call. replaceCallback() finds regex matches and returns the callback result for each one. substrReplace() replaces the part of the string at a position:

php
str('snake_case-name')->replace(['_', '-'], ' ');
// 'snake case name'

str('a1b2')->replaceCallback('/\d/', fn (array $m) => $m[0] * 2);
// 'a2b4'

str('hello')->substrReplace('J', 0, 1);
// 'Jello'

Filtering and Mapping Characters ​

map(), filter(), and reject() treat the string as a sequence of characters. Each callback receives one character at a time, and UTF-8 characters such as ü are passed whole. map() replaces each character with the callback result. filter() keeps the characters for which the callback returns true, and reject() keeps the others:

php
str('über')->map(fn (string $c) => $c . '|');
// 'ü|b|e|r|'

str('a1b2')->filter(fn (string $c) => $c !== '1');
// 'ab2'

str('hello')->reject(
    fn (string $c) => in_array($c, ['a', 'e', 'i', 'o', 'u'], true)
);
// 'hll'

When no character is kept, the result is an empty string.

TIP

Use replace() for fixed substrings and replaceCallback() for regex matches. Use map() and filter() when each character is decided on its own.

Slicing and Substrings ​

slice() and substring() take a start and an end position, and the end is not included. first() and last() take a number of characters from either end. truncate() keeps a number of characters and then appends a suffix:

php
$s = str('hello world');

$s->slice(0, 5);        // 'hello'
$s->substring(1, 3);    // 'el'
$s->first(2);           // 'he'
$s->last(2);            // 'ld'
$s->getChar(4);         // 'o'
$s->truncate(8, '...'); // 'hello wo...'

between() returns the text between two markers. insert(), removeChar(), and reverse() change characters by position:

php
str('[a]')->between('[', ']');  // 'a'
str('abc')->insert('X', 1);     // 'aXbc'
str('abc')->removeChar(1);      // 'ac'
str('abc')->reverse();          // 'cba'

Cleaning HTML ​

stripHtmlTags() removes HTML tags from a string. Pass tag names to keep them, as in '<b>':

php
str('<p>a <b>b</b></p>')->stripHtmlTags();      // 'a b'
str('<p>a <b>b</b></p>')->stripHtmlTags('<b>'); // 'a <b>b</b>'

INFO

stripHtmlTags() removes the tags only. It does not sanitize attributes or scripts, so use an HTML purifier for untrusted markup.

Splitting and Converting ​

explode() returns an ArrayObject. chop() splits the string into chunks of a given length and also returns an ArrayObject:

php
str('a,b,c')->explode(',');  // ArrayObject ['a', 'b', 'c']
str('abcdefg')->chop(3);     // ArrayObject ['abc', 'def', 'g']

To read a number, use toInteger() or toFloat(). To parse JSON, use jsonDecode(), which returns a Windwalker\Data\Collection:

php
str('42abc')->toInteger();   // 42
str('1.5')->toFloat();       // 1.5

str('{"a": 1}')->jsonDecode()->get('a'); // 1

The ArrayObject returned by explode() can be chained further, which is useful for parsing:

php
$values = str(' 1, 2 , , 3 ')
    ->explode(',')
    ->map(fn (string $v) => trim($v))
    ->filter()
    ->map(fn (string $v) => (int) $v)
    ->values();

// [1, 2, 3]

Inflection ​

The inflection methods detect and convert English singular and plural forms:

php
str('flowers')->isPlural();   // true
str('flower')->isSingular();  // true
str('flower')->toPlural();    // 'flowers'
str('flowers')->toSingular(); // 'flower'

Calls to the Str Helper ​

Any public static method of Windwalker\Utilities\Str can be called directly on a StringObject. The call passes the string as the first argument. The result is wrapped in a new StringObject when it is a string, and returned as-is otherwise:

php
str('Flowers')->startsWith('flo', false); // true, a native bool

Use explode() to split a string into an ArrayObject.

INFO

Calls to a name that does not exist throw a BadMethodCallException.

Encoding ​

The encoding decides how length(), indexOf(), and the other position methods count characters. Each instance has an encoding, and withEncoding() returns a copy with a different one:

php
str('über')->length();                 // 4, read as UTF-8
str('über', 'ISO-8859-1')->length();   // 5, read byte by byte
str('über', 'ISO-8859-1')
    ->withEncoding('UTF-8')
    ->length();                        // 4 again

WARNING

A string read with the wrong encoding produces silently wrong lengths and offsets. Set the encoding to match the source of the text, such as the file, database, or request charset.

Immutability ​

All transformations return a new instance, and withString() returns a copy with a different string:

php
$s = str('abc');
$u = $s->toUpperCase();

echo $s; // abc
echo $u; // ABC

The ArrayAccess methods are the exception. Writing to an offset changes the object in place:

php
$s = str('abc');
$s[0] = 'X';

echo $s; // Xbc

WARNING

$s[0] = 'X' and unset($s[0]) change the object itself, not a copy. Other StringObject values that share the same instance see the change. Use replace() or substrReplace() when a new value is needed.

To get the native string, use getString() or cast with (string):

php
$native = str('abc')->getString();
$native = (string) str('abc');

Released under the MIT License.