Generate Perfect Initials using PHP

PHP Initials Code Snapshot

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 will save us time in unexpected outcomes and make our code more reliable. I can see four basic requirements for our generator.

  1. Name has two words
    • eg: Chris Blackwell should translate to CB
  2. Name has only one word
    • eg: Chris should translate to CH
  3. Name has one word with two capitals.
    • eg: MacDonald should translate to MD
  4. 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 called $name that must be a string and returns a string. Type hinting protected us from other coders trying to pass unexpected types to our code.

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 called $name that must be a string and the function returns a string.

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');
    }
}

Comments

  1. 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]);

  2. 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));
    }
    }

  3. 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);
    ?>

  4. 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);

    1. 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);

  5. 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.

  6. 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)

  7. 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

Leave a Reply

Your email address will not be published. Required fields are marked *