Skip to content

Instantly share code, notes, and snippets.

@hipsterjazzbo
Last active March 24, 2022 15:29
Show Gist options
  • Save hipsterjazzbo/2532c93a18db7451b0cec529c95b53c4 to your computer and use it in GitHub Desktop.
Save hipsterjazzbo/2532c93a18db7451b0cec529c95b53c4 to your computer and use it in GitHub Desktop.
😈
<?php
class TitleCaser
{
protected static $smallWords = [
'(?<!q&)a',
'an',
'and',
'as',
'at(?!&t)',
'but',
'by',
'en',
'for',
'if',
'in',
'of',
'on',
'or',
'the',
'to',
'v[.]?',
'via',
'vs[.]?',
];
public static function toTitleCase($str)
{
$smallWordsRx = implode('|', self::$smallWords);
$apostropheRx = '(?x: [\'’] [[:lower:]]* )?';
$str = (new StrProxy($str))->trim();
if (preg_match('/[[:lower:]]/', $str) === 0) {
$str = $str->lower();
}
// The main substitutions
$str = preg_replace_callback(
'~\b (_*) (?: # 1. Leading underscore and
( (?<=[ ][/\\\\]) [[:alpha:]]+ [-_[:alpha:]/\\\\]+ | # 2. file path or
[-_[:alpha:]]+ [@.:] [-_[:alpha:]@.:/]+ ' . $apostropheRx . ' ) # URL, domain, or email
|
( (?i: ' . $smallWordsRx . ' ) ' . $apostropheRx . ' ) # 3. or small word (case-insensitive)
|
( [[:alpha:]] [[:lower:]\'’()\[\]{}]* ' . $apostropheRx . ' ) # 4. or word w/o internal caps
|
( [[:alpha:]] [[:alpha:]\'’()\[\]{}]* ' . $apostropheRx . ' ) # 5. or some other word
) (_*) \b # 6. With trailing underscore
~ux',
function ($matches) {
// Preserve leading underscore
$str = $matches[1];
if ($matches[2]) {
// Preserve URLs, domains, emails and file paths
$str .= $matches[2];
} elseif ($matches[3]) {
// Lower-case small words
$str .= (new StrProxy($matches[3]))->lower();
} elseif ($matches[4]) {
// Capitalize word w/o internal caps
$str .= (new StrProxy($matches[4]))->capitalize();
} else {
// Preserve other kinds of word (iPhone)
$str .= $matches[5];
}
// Preserve trailing underscore
$str .= $matches[6];
return $str;
},
$str);
// Exceptions for small words: capitalize at start of title...
$str = preg_replace_callback(
'~( \A [[:punct:]]* # start of title...
| [:.;?!][ ]+ # or of subsentence...
| [ ][\'"β€œβ€˜(\[][ ]* ) # or of inserted subphrase...
( ' . $smallWordsRx . ' ) \b # ...followed by small word
~uxi',
function ($matches) {
return $matches[1] . (new StrProxy($matches[2]))->capitalize();
},
$str
);
// ...and end of title
$str = preg_replace_callback(
'~\b ( ' . $smallWordsRx . ' ) # small word...
(?= [[:punct:]]* \Z # ...at the end of the title...
| [\'"’”)\]] [ ] ) # ...or of an inserted subphrase?
~uxi',
function ($matches) {
return (new StrProxy($matches[1]))->capitalize();
},
$str
);
// Exceptions for small words in hyphenated compound words
// e.g. "in-flight" -> In-Flight
$str = preg_replace_callback(
'~\b
(?<! -) # Negative lookbehind for a hyphen; we do not want to match man-in-the-middle but do want (in-flight)
( ' . $smallWordsRx . ' )
(?= -[[:alpha:]]+) # lookahead for "-someword"
~uxi',
function ($matches) {
return (new StrProxy($matches[1]))->capitalize();
},
$str
);
// e.g. "Stand-in" -> "Stand-In" (Stand is already capped at this point)
$str = preg_replace_callback(
'~\b
(?<!…) # Negative lookbehind for a hyphen; we do not want to match man-in-the-middle but do want (stand-in)
( [[:alpha:]]+- ) # $1 = first word and hyphen, should already be properly capped
( ' . $smallWordsRx . ' ) # ...followed by small word
(?! - ) # Negative lookahead for another -
~uxi',
function ($matches) {
return $matches[1] . (new StrProxy($matches[2]))->capitalize();
},
$str
);
return $str;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment