String helpers

The text a page shows is often made in its PHP first: a title cut to fit a card, a column name turned into a label, a slug for a database column, a token for a mail. PHP's own substr and ucwords count bytes and know only ASCII; the padStr functions count characters, so an accented letter or an emoji is never cut in half. They are plain functions, loaded on every request like the rest of pad/lib/.

function what it answers
padStrLimit ( $text, $limit = 100, $end = '...' ) at most $limit characters, $end added when something was cut
padStrWords ( $text, $words = 100, $end = '...' ) at most $words words
padStrExcerpt ( $text, $phrase, $radius = 100, $omission = '...' ) the phrase with $radius characters on either side, found case-insensitively
padStrSquish ( $text ) trimmed, every run of whitespace one space
padStrCamel, padStrStudly, padStrSnake ( $text, $delimiter = '_' ), padStrKebab, padStrHeadline userName, UserName, user_name, user-name, User Name - from a name in any of those cases
padStrTitle ( $text ) every word with a capital
padStrSlug ( $text, $separator = '-' ) a readable URL part - what the slug pipe gives
padStrAfter, padStrAfterLast, padStrBefore, padStrBeforeLast ( $text, $search ) the text after or before the first or last occurrence - what the pipes of those names give
padStrBetween ( $text, $from, $to ) what stands between the first $from and the last $to
padStrIs ( $pattern, $value ) whether the value matches a pattern with * wildcards, or one of a list
padStrMask ( $text, $character, $index, $length = NULL ) a stretch of the text replaced by one character
padStrPlural ( $word, $count = 2 ), padStrSingular ( $word ) the English plural or singular of a word
padStrRandom ( $length = 16 ) random letters and digits
padStrUuid ( $version = 4 ) a version 4 (random) or version 7 (time-ordered) UUID

Shortening text

padStrLimit cuts at a number of characters and padStrWords at a number of words; both trim the space the cut leaves, add $end only when something was cut, and give a text that fits back as it is. padStrExcerpt is the snippet of a search result: the place the phrase stands, with the text around it. padStrSquish makes text pasted into a form field compare and store the same way, tabs, newlines and no-break spaces included.

PHP
fragments/strings_1.php
HTML
fragments/strings_1.pad
Result
<?php

$text
= 'PAD templates drive the flow: data, logic and output '
. 'all follow the template.';

$limit = padStrLimit ( $text, 20 );
$words = padStrWords ( $text, 4 );
$excerpt = padStrExcerpt ( $text, 'LOGIC', 10 );
$squish = padStrSquish ( " data,\n\t logic and output " );

?>
<p>{$limit}</p>
<p>{$words}</p>
<p>{$excerpt}</p>
<p>[{$squish}]</p>

PAD templates drive...

PAD templates drive the...

...ow: data, logic and outpu...

[data, logic and output]

Names in every case

The case functions read the words of a name whatever case it is in - an underscore, a dash, a space or a change of case separates them - and write them in the case asked for, so created_at, createdAt and CreatedAt all turn into each other. padStrHeadline makes a label of a column name; padStrTitle recases a sentence and leaves the rest of it as it is.

PHP
fragments/strings_2.php
HTML
fragments/strings_2.pad
Result
<?php

$names
= [ 'created_at', 'orderTotal',
'customer-name', 'HTMLParser' ];

$columns = [];

foreach (
$names as $column )
$columns [] = [
'column' => $column,
'camel' => padStrCamel ( $column ),
'studly' => padStrStudly ( $column ),
'snake' => padStrSnake ( $column ),
'kebab' => padStrKebab ( $column ),
'headline' => padStrHeadline ( $column )
];

$sentence = padStrTitle ( 'the quick brown fox' );

?>
<table>
<tr><th>name</th><th>camel</th>
<th>studly</th><th>snake</th>
<th>kebab</th><th>headline</th></tr>
{columns}
<tr><td>{$column}</td><td>{$camel}</td>
<td>{$studly}</td><td>{$snake}</td>
<td>{$kebab}</td><td>{$headline}</td></tr>
{/columns}
</table>
<p>{$sentence}</p>
name camel studly snake kebab headline
created_at createdAt CreatedAt created_at created-at Created At
orderTotal orderTotal OrderTotal order_total order-total Order Total
customer-name customerName CustomerName customer_name customer-name Customer Name
HTMLParser htmlParser HtmlParser html_parser html-parser Html Parser

The Quick Brown Fox

Slugs and cutting at a marker

padStrSlug is the slug pipe - the pipe calls it - so a slug stored when a post is saved is the slug a template makes for its link. The same holds for after, afterLast, before and beforeLast: a text without the search string comes back unchanged. padStrBetween takes what follows the first $from up to the last $to.

PHP
fragments/strings_3.php
HTML
fragments/strings_3.pad
Result
<?php

$post
= 'Crème Brûlée & Co. - a recipe';
$slug = padStrSlug ( $post );

$file = 'shop/orders/list.pad';
$top = padStrBefore ( $file, '/' );
$folder = padStrBeforeLast ( $file, '/' );
$base = padStrAfterLast ( $file, '/' );

$html = '<title>Orders</title>';
$heading = padStrBetween ( $html, '<title>', '</title>' );

?>
<p>{$slug} - the pipe: {$post | slug}</p>
<p>{$top} | {$folder} | {$base}</p>
<p>{$heading}</p>

creme-brulee-co-a-recipe - the pipe: creme-brulee-co-a-recipe

shop | shop/orders | list.pad

Orders

Patterns and masks

padStrIs matches a value against a pattern in which * stands for any run of characters - a list of patterns matches when one of them does - without writing a regular expression. padStrMask hides part of a text: a negative index counts from the end, a negative length stops that many characters before it.

PHP
fragments/strings_4.php
HTML
fragments/strings_4.pad
Result
<?php

$allowed
= [ 'admin/*', 'reports/*.pdf' ];

$paths = [ 'admin/users',
'reports/may.pdf',
'reports/may.csv' ];

$checks = [];

foreach (
$paths as $path )
$checks [] = [
'path' => $path,
'allowed' => padStrIs ( $allowed, $path ) ? 'yes' : 'no'
];

$card = padStrMask ( '4111 1111 1111 1234', '*', 0, -4 );
$email = padStrMask ( 'ann.smith@example.com', '*', 1, 8 );

?>
{checks}
<p>{$path}: {$allowed}</p>
{/checks}
<p>{$card}</p>
<p>{$email}</p>

admin/users: yes

reports/may.pdf: yes

reports/may.csv: no

***************1234

a********@example.com

Plural and singular

padStrPlural gives the English plural of the last word of a text: the irregular words (person, child, mouse, knife ...), the uncountable ones (sheep, information, equipment ...) and the rules for -y, -s, -x, -ch and the rest. A count of one keeps the word, so "$n " . padStrPlural ( 'file', $n ) reads right for every number; an array or a Countable counts as its size. The case of the word is kept: Person, People; PERSON, PEOPLE. padStrSingular goes the other way.

PHP
fragments/strings_5.php
HTML
fragments/strings_5.pad
Result
<?php

$carts
= [];

foreach ( [
0, 1, 2 ] as $n )
$carts [] = [
'n' => $n,
'items' => padStrPlural ( 'item', $n ),
'people' => padStrPlural ( 'person', $n )
];

$nouns = [ 'city', 'child', 'knife', 'analysis',
'sheep', 'Person', 'blogPost' ];

$words = [];

foreach (
$nouns as $word )
$words [] = [
'word' => $word,
'plural' => padStrPlural ( $word ),
'back' => padStrSingular ( padStrPlural ( $word ) )
];

?>
{carts}
<p>{$n} {$items} for {$n} {$people}</p>
{/carts}
<table>
<tr><th>word</th><th>plural</th><th>singular again</th></tr>
{words}
<tr><td>{$word}</td><td>{$plural}</td><td>{$back}</td></tr>
{/words}
</table>

0 items for 0 people

1 item for 1 person

2 items for 2 people

word plural singular again
city cities city
child children child
knife knives knife
analysis analyses analysis
sheep sheep sheep
Person People Person
blogPost blogPosts blogPost

Random strings and UUIDs

padStrRandom draws letters and digits with random_int, PHP's secure generator - good for a token or a code to mail. padStrUuid makes a UUID as RFC 9562 writes it: version 4 is random; version 7 starts with the time, so ids made later sort later and a database index on them stays in order. The example shows their shape, as their value differs on every request.

PHP
fragments/strings_6.php
HTML
fragments/strings_6.pad
Result
<?php

$token
= padStrRandom ( 32 );
$first = padStrUuid ( 7 );
$later = padStrUuid ( 7 );
$id = padStrUuid ();

// Random on every request: what is shown is their shape,
// not their value.

$tokenShape = preg_match ( '/^[A-Za-z0-9]{32}$/', $token )
?
'32 letters and digits' : 'wrong';
$shape = preg_replace ( '/[0-9a-f]/', 'x', $id );
$versions = $id [14] . ' and ' . $first [14];
$ordered = strcmp ( $later, $first ) > 0 ? 'yes' : 'no';

?>
<p>token: {$tokenShape}</p>
<p>uuid: {$shape}, versions {$versions}</p>
<p>a later version 7 sorts after an earlier one: {$ordered}</p>

token: 32 letters and digits

uuid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, versions 4 and 7

a later version 7 sorts after an earlier one: yes

Wrong input

NULL and FALSE are read as an empty text and a number as its digits, so the functions take a database value as it comes. An array, a negative limit, an unknown UUID version or an empty mask character is a mistake of the author: it is reported as a PAD error naming the function, and the function answers an empty value.