skip to content

In Laravel, what do Str::slug(), Str::limit() and Str::mask() do to a product title and a card number on an order page?

level: juniorimportance: must knowfreq 55%

answer

  1. URL-safe, lower-case, separator '-'
  2. '@' becomes 'at' by default
  3. limit: 100 characters, then '...'
  4. mask: character, start index, length
  5. negative length keeps the tail

basics

~20 s

Str::slug() turns a title into a lower-case, dash-separated URL segment; Str::limit() cuts text to a character count and appends '...'; Str::mask() replaces part of a string with a repeated character, such as all but the last four card digits.

solid answer

~40 s

`Str::slug($title, $separator = '-', $language = 'en', $dictionary = ['@' => 'at'])` transliterates to ASCII, lower-cases, swaps `@` for `at`, drops other symbols and collapses spaces into single dashes, so `Café Crème 50% OFF!` becomes `cafe-creme-50-off`. `Str::limit($value, $limit = 100, $end = '...', $preserveWords = false)` returns the value unchanged if it fits, otherwise cuts at `$limit` characters of display width and appends `$end` — so the result can be longer than `$limit`; pass `preserveWords: true` to avoid cutting mid-word. `Str::mask($string, $character, $index, $length = null)` overwrites a slice with one repeated character; `Str::mask($card, '*', 0, -4)` keeps only the last four digits. All three are multibyte-safe and return plain strings.

code

php · 10 lines
php
<?php

use Illuminate\Support\Str;

$product = 'Café Crème Espresso Cups (Set of 4)';
$card    = '4111111111111111';

$slug    = Str::slug($product);                  // cafe-creme-espresso-cups-set-of-4
$summary = Str::limit($product, 19, preserveWords: true); // Café Crème Espresso...
$masked  = Str::mask($card, '*', 0, -4);         // ************1111

go deeper

for a junior

Recall what slug(), limit() and mask() return and their key defaults: the dash separator, the appended '...', and the negative-length trick for keeping the last digits.

for a middle

Explain the steps inside slug() — transliteration, the '@' dictionary, symbol stripping — and why limit() measures display width with an appended end string.

for a senior

Handle slug uniqueness with a unique index, keep masking as presentation only, and pick word-safe truncation where layout matters.

for a principal

Set conventions for URL identifiers and sensitive-data display so teams do not reinvent slugs or leak data behind cosmetic masking.

## Three helpers for one order page An order page typically shows a **product link**, a **short product name** in a summary table and a **masked card number**. Laravel's `Illuminate\Support\Str` class has a helper for each, and interviewers ask about them because hand-rolled versions are a classic source of bugs with accents, emoji and off-by-one slicing. ## Str::slug() Signature: `Str::slug($title, $separator = '-', $language = 'en', $dictionary = ['@' => 'at'])`. What it does, in order: 1. transliterates the title to ASCII for the given language (`é` becomes `e`), unless `$language` is `null`; 2. converts the *other* separator character (underscore when the separator is a dash) into the separator; 3. replaces dictionary entries — by default `@` becomes `at` surrounded by separators; 4. lower-cases the string and removes anything that is not a letter, digit, whitespace or the separator; 5. collapses runs of whitespace and separators into a single separator and trims it from both ends. ```php <?php use Illuminate\Support\Str; Str::slug('Café Crème 50% OFF!'); // cafe-creme-50-off Str::slug('Tom & Jerry @ Home'); // tom-jerry-at-home Str::slug('Blue Mug', '_'); // blue_mug ``` Two things `slug()` does **not** do: it does not make the slug **unique** (two products named "Blue Mug" get the same slug, so you check the table and append a suffix), and it does not keep `&` — only dictionary entries are translated. ## Str::limit() Signature: `Str::limit($value, $limit = 100, $end = '...', $preserveWords = false)`. - If the string's display width is within `$limit`, it is returned unchanged. - Otherwise it is cut to `$limit` columns, trailing whitespace is trimmed, and `$end` is **appended** — the ellipsis is not counted inside the limit. - Width is measured with `mb_strwidth`, so wide characters (many CJK characters) count as two columns. - With `preserveWords: true`, it strips tags, then cuts back to the last complete word. ```php <?php Str::limit('Wireless noise-cancelling headphones', 8); // Wireless... Str::limit('Wireless noise-cancelling headphones', 12, preserveWords: true); // Wireless... ``` For word counts rather than characters there is `Str::words($value, $words = 100, $end = '...')`. ## Str::mask() Signature: `Str::mask($string, $character, $index, $length = null, $encoding = 'UTF-8')`. | Call | Result | |---|---| | `Str::mask('4111111111111111', '*', 0, -4)` | `************1111` | | `Str::mask('4111111111111111', '*', -16, 12)` | `************1111` | | `Str::mask('[email protected]', '*', 1, 3)` | `j***@example.com` | | `Str::mask('4111', '', 0)` | `4111` (empty character: unchanged) | Rules worth remembering: - a **negative index** counts from the end; - a **negative length** stops that many characters before the end; - only the **first character** of `$character` is used; - slicing is multibyte-aware, so names with accents mask correctly. ## The Stringable forms and related helpers Each helper has a fluent twin on `Str::of()`, useful when several steps run in a row: ```php <?php $link = Str::of($product->name)->squish()->slug()->prepend('/products/')->toString(); ``` Related helpers that come up in the same conversation: - `Str::words($value, $words = 100, $end = '...')` truncates by **word count** instead of width; - `Str::squish()` collapses internal runs of whitespace before you slug or truncate; - `Str::ascii()` is the transliteration step `slug()` uses, available on its own for search keys; - `Str::excerpt()` pulls a snippet around a search phrase, which suits search results better than `limit()`. Pick the helper by what the **reader** needs: a stable URL segment (`slug`), a fixed-width label (`limit`), a readable teaser (`words`), or a privacy-preserving display (`mask`). ## Masking is presentation, not protection `Str::mask()` changes what the page shows. It does nothing about what you **store** or **log**: a full card number should never reach your database or logs in the first place, and a masked string in the view does not make it acceptable to pass the full one to the template. Treat the helper as the last formatting step on data that is already safe to display. ## Why interviewers ask The question checks that a candidate reaches for framework helpers instead of `strtolower(str_replace(' ', '-', $title))`, which breaks on accents, punctuation and repeated spaces, and that they know the small defaults — the dash separator, the `@` dictionary, the appended ellipsis, the negative-length trick — that decide what users actually see.

  • Does Laravel's Str::slug() guarantee a unique slug for each product?
    No. It is a pure string transformation, so two products with the same name get the same slug. Uniqueness needs a check against the table — typically appending `-2`, `-3` or a short random suffix — and a unique index on the column so a race cannot slip a duplicate through.
  • Why can Str::limit($name, 20) return a string longer than 20 characters in Laravel?
    Because `$end`, `'...'` by default, is appended after the cut rather than counted inside the limit. `Str::limit('Wireless noise-cancelling', 8)` returns `Wireless...`, eleven characters. Pass a shorter `$end` or a smaller limit if the column width is strict.

saying these in an interview costs you the question

  • Builds slugs with strtolower and str_replace on spaces.
  • Assumes Str::slug() keeps the result unique across products.
  • Thinks Str::limit() counts the '...' inside the limit.
  • Believes Str::mask() makes it safe to store full card numbers.
  • Says Str::slug() turns '&' into 'and' by default.