-
-
Save hipsterjazzbo/2532c93a18db7451b0cec529c95b53c4 to your computer and use it in GitHub Desktop.
π
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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