Re: [rfc] str_mask function
| From: | سپهر محمودی | Date: | Fri, 18 Sep 2026 20:51:21 +0000 |
| Subject: | Re: [rfc] str_mask function | ||
| References: | 1 2 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-132545@lists.php.net to get a copy of this message | ||
در تاریخ جمعه ۱۸ سپتامبر ۲۰۲۶، ۲۳:۱۲ Osama Aldemeery
<aldemeery@gmail.com>
نوشت:
> On Fri, Sep 18, 2026 at 9:27 PM سپهر محمودی
> <sepehrphpr@gmail.com>
> wrote:
> >
> > Hi everyone,
> >
> > I'm officially starting my work on the ext/intl and standard string
> functions, and I'm very excited to share my first major proposal for PHP
> 8.7!
> >
> > I have successfully set up my local development environment and compiler
> on my machine, and everything is up and running smoothly.
> >
> > As part of this, I would like to propose a new native function called
> str_mask().
> >
> > ### Proposal Overview
> > The str_mask() function is designed to securely mask portions of a
> string using a specified mask character. This is extremely useful for
> handling sensitive user data like credit card numbers, phone numbers, and
> tokens.
> >
> > Signature:
> > str_mask(string $string, string $mask_char = '*', int $offset = 0, ?int
> $length = null): string
> >
> > ### Examples
> > 1. Masking a credit card (positive offset & length):
> > $credit_card = '1234567890123456';
> > $masked_card = str_mask($credit_card, '*', 4, 8);
> > // Output: 1234********3456
> >
> > 2. Masking a phone number (negative offset to count from the end):
> > $phone_number = '+989123456789';
> > $masked_phone = str_mask($phone_number, 'X', -4);
> > // Output: +9891234XXXX
> >
> > You can find all the details, implementation plans, and RFC discussions
> here:
> > https://wiki.php.net/rfc/str_mask
> >
> > Looking forward to hearing your feedback and thoughts!
> >
> > Best regards,
> > Sepehr Mahmoudi
>
> Hi,
>
> Unless I am missing something...this looks identical to the existing
>
substr_replace. It has the same signature with just a different
> name
> and without array being part of the params/return types.
>
> Your examples could be rewritten as:
>
> ```
> $credit_card = '1234567890123456';
> $masked_card = substr_replace($credit_card, str_repeat('*', 8), 4, 8);
>
> $phone_number = '+989123456789';
> $masked_phone = substr_replace($phone_number, str_repeat('X', 4), -4);
> ```
>
> Regards,
> Osama
>
--------
Hi Osama,
Thank you for the feedback!
While it is true that masking can be composed using `substr_replace($str,
str_repeat($mask, $len), $offset, $len)`, there are three key motivations
behind proposing str_mask():
1. Ergonomics & Clarity: Masking sensitive information (PII, credit cards,
emails, tokens) is one of the most common everyday tasks in modern web
security and logging. Writing substr_replace(..., str_repeat(...), ...)
is verbose and prone to off-by-one errors. A dedicated function makes the
intent clear.
2. Fail-Closed Security & Strict Validation: Data masking often deals with
sensitive credentials. substr_replace() has legacy and lenient behaviors
regarding out-of-bounds offsets. In contrast, str_mask() is designed with
a strict fail-closed approach (throwing ValueError on invalid boundaries
or empty mask characters) to ensure sensitive data is never silently
exposed due to silent clipping.
3. Performance & Memory: The substr_replace + str_repeat combination
performs two separate string allocations (one temporary string created by
str_repeat and the final string created by substr_replace).
str_mask() computes the masked string directly in C in a single
allocation pass, making it more memory- and CPU-efficient.
Best regards,
Sepehr