Update March 26, 2022
I’ve updated this to be compliant with UTF-8 characters.
Most of the web apps you use have a placeholder image for your avatar. If the app is using Gravatar to generate the avatars, you will get the mystery man if no user exists. Applications like Trello take it to the next level by displaying initials of the user if they have no avatar. Today we are going to learn how to generate those using a few lines of clean PHP.
Scenarios
First, we need to layout is all the possible ways we want to generate initials, and what the expected outcome would be. Defining our requirements up front
Name has two words- eg: Chris Blackwell should translate to CB
Name has only one word- eg: Chris should translate to CH
N ame has one word with two capitals.- eg: MacDonald should translate to MD
- Name has more than two words.
- eg: Chris Shane Blackwell should translate to CB
Did I miss any? If so please leave a comment and I’ll update the article.
Creating our Class
I like to get things started by stubbing out any methods I normally need. Let’s create our class and stub out any methods we need. When I’m using a utility class like this, I make a generate
method so I know what the primary action is.
class Initials
{
/**
* Generate initials from a name
*
* @param string $name
* @return string
*/
public function generate(string $name) : string
{
// @TODO
}
}
The generate method takes one parameter $name
The first thing we need to do is break the name into an array of words.
$words = explode(' ', $name);
Any string we pass into as the $name
parameter will give us an array of all the words in the string. Passing my name of Chris Blackwell will give us this array:
array(2) {
[0]=>
string(5) "Chris"
[1]=>
string(9) "Blackwell"
}
Looking at this array gives us insight into our easiest route to success. Taking the first letter of the each of the words in the array will give us our initials. We can use our $words
array and take care of our basic requirement of a full name with one line:
public function generate(string $name) : string
{
$words = explode(' ', $name);
return mb_strtoupper(
mb_substr($words[0], 0, 1, 'UTF-8') .
mb_substr(end($words), 0, 1, 'UTF-8'),
'UTF-8');
}
This line takes the first letter of the first word and the first letter of the last word. It wraps those methods in a strtoupper
method to capitalize the initials if they were not already.
While this covers most of the scenarios we will see, we still have the single word case “Chris” and the scenario where things are capitalized in one word “MacDonald”.
Catching the Edge Cases
Since our code only works with names that have two words or more, we will wrap it in an if block to catch them.
public function generate(string $name) : string
{
$words = explode(' ', $name);
if (count($words) >= 2) {
return mb_strtoupper(
mb_substr($words[0], 0, 1, 'UTF-8') .
mb_substr(end($words), 0, 1, 'UTF-8'),
'UTF-8');
}
}
We’re going to add another method to make initials from a single word. I like naming my functions very descriptive, so we will call this function makeInitialsFromSingleWord. Exactly like our generate function, this one takes one parameter $name
protected function makeInitialsFromSingleWord(string $name) : string
{
// @TODO
}
The first thing we want to do in this function is filter out any capitals in the string. We will use the preg_match_all method to assign any capitals it finds to a variable called $capitals. If there are 2 or more capitals, we can use the first two for initials.
If there are less then 2 capitals, we will use the first two letters of the name and capitalize them.
protected function makeInitialsFromSingleWord(string $name) : string
{
preg_match_all('#([A-Z]+)#', $name, $capitals);
if (count($capitals[1]) >= 2) {
return mb_substr(implode('', $capitals[1]), 0, 2, 'UTF-8');
}
return mb_strtoupper(mb_substr($name, 0, 2, 'UTF-8'), 'UTF-8');
}
Summary
We now have the ability to create initials just like you see on all your Trello cards. All four of our name scenarios have been verified to work. Do you think there is a way we could have extended this to cover more names? I’d love to hear your feedback!
Below is the code in it’s entirety
class Initials
{
/**
* Generate initials from a name
*
* @param string $name
* @return string
*/
public function generate(string $name) : string
{
$words = explode(' ', $name);
if (count($words) >= 2) {
return mb_strtoupper(
mb_substr($words[0], 0, 1, 'UTF-8') .
mb_substr(end($words), 0, 1, 'UTF-8'),
'UTF-8');
}
return $this->makeInitialsFromSingleWord($name);
}
/**
* Make initials from a word with no spaces
*
* @param string $name
* @return string
*/
protected function makeInitialsFromSingleWord(string $name) : string
{
preg_match_all('#([A-Z]+)#', $name, $capitals);
if (count($capitals[1]) >= 2) {
return mb_substr(implode('', $capitals[1]), 0, 2, 'UTF-8');
}
return mb_strtoupper(mb_substr($name, 0, 2, 'UTF-8'), 'UTF-8');
}
}
everytime you use regex a baby dies.
fsaf
I think you can replace this line of code:
return strtoupper(substr($words[0], 0, 1) . substr(end($words), 0, 1));
to this:
return strtoupper($words[0][0] . end($words)[0]);
That does not support UTF-8.
Code fails badly on strings for UTF8 two and higher byte characters, like Chinese, Japanese, etc.
Here is what we currently have.
class Initials
{
/**
* Generate initials from a name
*
* @param string $name
* @return string
*/
public static function generate(string $name) : string
{
$words = explode(‘ ‘, $name);
if (count($words) >= 2) {
$initials = mb_strtoupper(mb_substr($words[0], 0, 1));
$initials .= mb_strtoupper(mb_substr(end($words), 0, 1));
return $initials;
}
return self::makeInitialsFromSingleWord($name);
}
/**
* Make initials from a word with no spaces
*
* @param string $name
* @return string
*/
protected static function makeInitialsFromSingleWord(string $name) : string
{
preg_match_all(‘#([A-Z]+)#’, $name, $capitals);
if (count($capitals[1]) >= 2) {
return mb_substr(implode(”, $capitals[1]), 0, 2);
}
return mb_strtoupper(mb_substr($name, 0, 2));
}
}
Mr. Jon Doe -> JD
save the following code as index.php
and make a GET request wit name parameter
http://localhost/index.php?name=CodingFrontend
Then see the magic 🙂
generate($_GET[‘name’]);
$bgColor = [104, 159, 57];
$textColor = [255, 255, 255];
$fontSize = 140;
$width = 600;
$height = 600;
$font = ‘./Roboto.ttf’;
$image = @imagecreate($width, $height) or die(“Cannot Initialize new GD image stream”);
imagecolorallocate($image, $bgColor[0], $bgColor[1], $bgColor[2]);
$fontColor = imagecolorallocate($image, $textColor[0], $textColor[1], $textColor[2]);
$textBoundingBox = imagettfbbox($fontSize, 0, $font, $text);
$y = abs(ceil(($height – $textBoundingBox[5]) / 2));
$x = abs(ceil(($width – $textBoundingBox[2]) / 2));
imagettftext($image, $fontSize, 0, $x, $y, $fontColor, $font, $text);
return $image;
}
class Initials{
public function generate($name){
$words = explode(‘ ‘, $name);
if (count($words) >= 2) {
return strtoupper(substr($words[0], 0, 1) . substr(end($words), 0, 1));
}
return $this->makeInitialsFromSingleWord($name);
}
protected function makeInitialsFromSingleWord($name){
preg_match_all(‘#([A-Z]+)#’, $name, $capitals);
if (count($capitals[1]) >= 2) {
return substr(implode(”, $capitals[1]), 0, 2);
}
return strtoupper(substr($name, 0, 2));
}
}
$img = createDefaultAvatar();
header(“Content-Type: image/png”);
imagepng($img);
imagedestroy($img);
?>
Beautiful piece! It returns the first letter of each of the first and the last words in the name for multi-word names and the first 2 letters from a single-word name.
I modified it slightly to return create the initials from the first letter of EVERY word in a multi-word name.
Just replace the content generate methos/function with the following code:
$words = explode(‘ ‘, $name);
//$initials = strtoupper(substr($words[0], 0, 1) . substr(end($words), 0, 1));
$initials = strtoupper( substr( $words[0], 0, 1 ) );
if (count($words) >= 2) {
for ( $i = 1; $i makeInitialsFromSingleWord($name);
Beautiful piece! It returns the first letter of each of the first and the last words in the name for multi-word names and the first 2 letters from a single-word name.
I modified it slightly to create the initials from the first letter of EVERY word in a multi-word name.
Just replace the content of the generate() method/function with the following code:
$words = explode(‘ ‘, $name);
//$initials = strtoupper(substr($words[0], 0, 1) . substr(end($words), 0, 1));
$initials = strtoupper( substr( $words[0], 0, 1 ) );
if (count($words) >= 2) {
for ( $i = 1; $i makeInitialsFromSingleWord($name);
I have copied the code at 5. above from CodingFrontend into a file called iconify.php on my webserver running PHP7.2, and then visit iconify.php?name=Fred
The result I get is a blank page 🙁
The page can be seen at personality.co.uk/psf1/iconify.php
BTW: I saw the code ended ?> so I added a <?php above the first line as I assumed that was needed.
Made a regex that splits almost any complex and odd last names like “O’Ryan”, “DeMarco” or “Rodriguez-Lopez Vasquez-Garcia”…
preg_split(“#(?<=[a-z])(?=[A-Z])|['’ -]+#", $lastName)
Why don’t you update your code or state explicitely this works ONLY for 8-bit coding? You are misleading people instead of helping them
Other solution that works with accent :
function extractMultipleWordsInitials($sentence) {
$expr = ‘/(?<=(\s|-)|^)\w/iu'; // uses lookbehind … https://www.regular-expressions.info/lookaround.html#lookbehind, src : https://stackoverflow.com/a/16165234/6614155
preg_match_all($expr, $sentence, $matches);
$result = implode('', $matches[0]);
return mb_strtoupper($result);
}