Skip to content

Instantly share code, notes, and snippets.

@timvisee
Last active January 5, 2024 16:29
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save timvisee/2ad754445ea63262eb3b903864c641cc to your computer and use it in GitHub Desktop.
Save timvisee/2ad754445ea63262eb3b903864c641cc to your computer and use it in GitHub Desktop.

As posted on https://timvisee.com/blog/snippet-correctly-capitalize-names-in-php/

Correctly capitalize names in PHP

This will capitalize/normalize names in PHP. Use with care, because this makes some assumptions even though there is no consistency in naming.

<?php

/**
  * Normalize the given (partial) name of a person.
  *
  * - re-capitalize, take last name inserts into account
  * - remove excess white spaces
  *
  * Snippet from: https://timvisee.com/blog/snippet-correctly-capitalize-names-in-php
  *
  * @param string $name The input name.
  * @return string The normalized name.
  */
function name_case($name) {
    // A list of properly cased parts
    $CASED = [
      "O'", "l'", "d'", 'St.', 'Mc', 'the', 'van', 'het', 'in', "'t", 'ten',
      'den', 'von', 'und', 'der', 'de', 'da', 'of', 'and', 'the', 'III', 'IV',
      'VI', 'VII', 'VIII', 'IX',
    ];

    // Trim whitespace sequences to one space, append space to properly chunk
    $name = preg_replace('/\s+/', ' ', $name) . ' ';

    // Break name up into parts split by name separators
    $parts = preg_split('/( |-|O\'|l\'|d\'|St\\.|Mc)/i', $name, -1, PREG_SPLIT_DELIM_CAPTURE);

    // Chunk parts, use $CASED or uppercase first, remove unfinished chunks
    $parts = array_chunk($parts, 2);
    $parts = array_filter($parts, function($part) {
            return sizeof($part) == 2;
        });
    $parts = array_map(function($part) use($CASED) {
            // Extract to name and separator part
            list($name, $separator) = $part;

            // Use specified case for separator if set
            $cased = current(array_filter($CASED, function($i) use($separator) {
                return strcasecmp($i, $separator) == 0;
            }));
            $separator = $cased ? $cased : $separator;

            // Choose specified part case, or uppercase first as default
            $cased = current(array_filter($CASED, function($i) use($name) {
                return strcasecmp($i, $name) == 0;
            }));
            return [$cased ? $cased : ucfirst(strtolower($name)), $separator];
        }, $parts);
    $parts = array_map(function($part) {
            return implode($part);
        }, $parts);
    $name = implode($parts);

    // Trim and return normalized name
    return trim($name);
}

Here is the same, but optimized for Laravel using Laravel collections:

<?php

/**
  * Normalize the given (partial) name of a person.
  *
  * - re-capitalize, take last name inserts into account
  * - remove excess white spaces
  *
  * Snippet from: https://timvisee.com/blog/snippet-correctly-capitalize-names-in-php
  *
  * @param string $name The input name.
  * @return string The normalized name.
  */
function name_case($name) {
    // A list of properly cased parts
    $CASED = collect([
        "O'", "l'", "d'", 'St.', 'Mc', 'the', 'van', 'het', 'in', "'t", 'ten',
        'den', 'von', 'und', 'der', 'de', 'da', 'of', 'and', 'the', 'III', 'IV',
        'VI', 'VII', 'VIII', 'IX',
    ]);

    // Trim whitespace sequences to one space, append space to properly chunk
    $name = preg_replace('/\s+/', ' ', $name) . ' ';

    // Break name up into parts split by name separators
    $parts = preg_split('/( |-|O\'|l\'|d\'|St\\.|Mc)/i', $name, -1, PREG_SPLIT_DELIM_CAPTURE);

    // Chunk parts, use $CASED or uppercase first, remove unfinished chunks
    $name = collect($parts)
        ->chunk(2)
        ->filter(function($part) {
            return $part->count() == 2;
        })
        ->mapSpread(function($name, $separator = null) use($CASED) {
            // Use specified case for separator if set
            $cased = $CASED->first(function($i) use($separator) {
                return strcasecmp($i, $separator) == 0;
            });
            $separator = $cased ?? $separator;

            // Choose specified part case, or uppercase first as default
            $cased = $CASED->first(function($i) use($name) {
                return strcasecmp($i, $name) == 0;
            });
            return [$cased ?? ucfirst(strtolower($name)), $separator];
        })
        ->map(function($part) {
            return implode($part);
        })
        ->join('');

    // Trim and return normalized name
    return trim($name);
}
@julienbornstein
Copy link

use mb_strtolower to support accents otherwise :

-> name_case("ELYSÉE")
"ElysÉe"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment