[PHP-DEV] [rfc] str_mask function

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 Sepehr,

I have a few concerns with the current semantics before this becomes another permanent global API.

The RFC motivates str_mask() primarily around sensitive data and privacy, but the proposed behavior is fail-open:

str_mask(‘AB’, ‘*’, 3) === ‘AB’

An incorrect offset therefore returns the original sensitive value unchanged.

If masking is being presented as the reason for introducing the function, why is an invalid masking range considered successful rather than exceptional? Silently leaking the input seems like a particularly surprising contract for an API whose stated purpose is hiding data.

There is a similar issue with $mask_char.

The signature accepts string, but the RFC specifies that only the first byte is used. So:

str_mask($value, ‘●’, …)

does not mask using the character that was passed. It takes one byte from a multibyte UTF-8 sequence and can produce invalid UTF-8.

I understand that str_mask() is intended to be byte-oriented, but then accepting an arbitrary string and silently truncating it to one byte feels like the wrong API. Why not require exactly one byte and throw ValueError otherwise?

My larger concern, though, is whether the primitive itself earns a place in core.

The RFC’s email example still needs strpos() and strlen() to determine where the semantic part to hide actually is. Credit cards, emails, phone numbers, tokens and identifiers all have different disclosure rules. PHP cannot determine those rules; the caller still has to do that work.

Once the caller already knows the offset and length, the remaining operation is essentially replacement of a substring with a repeated byte.

So I think there are three questions that need stronger answers:

  1. What recurring capability does str_mask() provide that cannot already be expressed clearly with existing string primitives?

  2. Do we have evidence from real-world codebases that this pattern occurs frequently enough to justify another global function rather than a small userland abstraction?

  3. The RFC states that the implementation is “significantly faster”. Could you include reproducible benchmarks against an equivalent substr_replace()/str_repeat() implementation, including short strings representative of the examples in the RFC?

For operations on strings this small, C being faster in isolation does not necessarily establish that the difference is meaningful enough to justify expanding the standard library.

I would also avoid describing this as providing privacy or GDPR compliance. Whether exposing part of an identifier is appropriate is contextual, and a generic byte-level replacement operation cannot provide that guarantee.

I think these questions should be resolved before discussing the implementation itself.

Best regards,
Pratik Bhujel

‪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:
PHP: 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

در تاریخ جمعه ۱۸ سپتامبر ۲۰۲۶، ۲۲:۲۳ Pratik Bhujel <prateekbhujelpb@gmail.com> نوشت:

Hi Sepehr,

I have a few concerns with the current semantics before this becomes another permanent global API.

The RFC motivates str_mask() primarily around sensitive data and privacy, but the proposed behavior is fail-open:

str_mask(‘AB’, ‘*’, 3) === ‘AB’

An incorrect offset therefore returns the original sensitive value unchanged.

If masking is being presented as the reason for introducing the function, why is an invalid masking range considered successful rather than exceptional? Silently leaking the input seems like a particularly surprising contract for an API whose stated purpose is hiding data.

There is a similar issue with $mask_char.

The signature accepts string, but the RFC specifies that only the first byte is used. So:

str_mask($value, ‘●’, …)

does not mask using the character that was passed. It takes one byte from a multibyte UTF-8 sequence and can produce invalid UTF-8.

I understand that str_mask() is intended to be byte-oriented, but then accepting an arbitrary string and silently truncating it to one byte feels like the wrong API. Why not require exactly one byte and throw ValueError otherwise?

My larger concern, though, is whether the primitive itself earns a place in core.

The RFC’s email example still needs strpos() and strlen() to determine where the semantic part to hide actually is. Credit cards, emails, phone numbers, tokens and identifiers all have different disclosure rules. PHP cannot determine those rules; the caller still has to do that work.

Once the caller already knows the offset and length, the remaining operation is essentially replacement of a substring with a repeated byte.

So I think there are three questions that need stronger answers:

  1. What recurring capability does str_mask() provide that cannot already be expressed clearly with existing string primitives?

  2. Do we have evidence from real-world codebases that this pattern occurs frequently enough to justify another global function rather than a small userland abstraction?

  3. The RFC states that the implementation is “significantly faster”. Could you include reproducible benchmarks against an equivalent substr_replace()/str_repeat() implementation, including short strings representative of the examples in the RFC?

For operations on strings this small, C being faster in isolation does not necessarily establish that the difference is meaningful enough to justify expanding the standard library.

I would also avoid describing this as providing privacy or GDPR compliance. Whether exposing part of an identifier is appropriate is contextual, and a generic byte-level replacement operation cannot provide that guarantee.

I think these questions should be resolved before discussing the implementation itself.

Best regards,
Pratik Bhujel


Hi Pratik,

Thanks for the feedback! I’ve updated the RFC document to reflect the fail-closed error handling design and edge-case behavior we discussed.

Regarding grapheme_mask(), I haven’t designed or implemented it yet, but I will definitely incorporate these principles once I start working on it in ext/intl.

Also, to ensure this on it in ext/intl.

Also, to ensure this to include real-world usage analysis/benchmarks (e.g. log masking, sensitive data display in popular frameworks) in the proposal? I’d love to hear your thoughts on this or any specific cases you have in mind.

Best regards,
Sepehr

در تاریخ جمعه ۱۸ سپتامبر ۲۰۲۶، ۲۳:۱۲ 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

8On Fri, Sep 18, 2026, at 20:26, سپهر محمودی wrote:

As part of this, I would like to propose a new native function called str_mask().

Not useful.
Easily implemented in user-space.
Performance would never be an issue for a function typically used on small strings a few times per request.

In the multi-million-line web application I work on this probably would not be used even once.

Please stop this. Your mails read like AI slop, and your proposals are of very poor quality. I suspect that there are more than a few thousand humans reading this list, and you are wasting their time.

Please don’t see this as a personal attack. It’s the work that is not good enough, not you as a person.

Greetings, Casper

This. I don’t see any reason why this should added. Its use-case is quite a niche, which can be easily solved by calling substr_replace together with str_repeat.

If you want to go on with your idea, the string parameter of this function should have the SensitiveParameter attribute or else it may end up in a backtrace.

Regards,
Jordi

Hi,

  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.

I don’t think it is verbose. Perhaps different people have different coding styles. But I down vote on that statement.

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

Can you provide an actual example of out-of-bounds offsets and it’s impact for us to understand?

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

Well, I’d argue that the str_repeat here is basically for better readability. That

$phone_number = ‘+989123456789’;
$masked_phone = substr_replace($phone_number, str_repeat(‘X’, 4), -4);

Can be written to

$phone_number = ‘+989123456789’;
$masked_phone = substr_replace($phone_number, ‘XXXX’, -4);

So there is only one single allocation. The second one you are pointing to comes from repeating the string, which is not what we are arguing about here.

Cheers,
Weilin Du

در تاریخ شنبه ۱۹ سپتامبر ۲۰۲۶، ۰۲:۱۳ Casper Langemeijer <langemeijer@php.net> نوشت:

8On Fri, Sep 18, 2026, at 20:26, سپهر محمودی wrote:

As part of this, I would like to propose a new native function called str_mask().

Not useful.
Easily implemented in user-space.
Performance would never be an issue for a function typically used on small strings a few times per request.

In the multi-million-line web application I work on this probably would not be used even once.

Please stop this. Your mails read like AI slop, and your proposals are of very poor quality. I suspect that there are more than a few thousand humans reading this list, and you are wasting their time.

Please don’t see this as a personal attack. It’s the work that is not good enough, not you as a person.

Greetings, Casper


Hi Casper,

My responses to your questions are summarized in the benchmark tests, which I am currently conducting. I prefer to avoid any further discussion regarding AI to keep our focus strictly on the technical merits of the proposal.

Best regards,
Sepehr

This. I don’t see any reason why this should added. Its use-case is quite a niche, which can be easily solved by calling substr_replace together with str_repeat.

If you want to go on with your idea, the string parameter of this function should have the SensitiveParameter attribute or else it may end up in a backtrace.

Regards,
Jordi

Hi Sepehr,

I don’t think benchmarks actually answer the main objection being raised here. They can show that a dedicated C implementation is faster than composing substr_replace() and str_repeat(), but they cannot show that this operation deserves a permanent core API.

Before optimizing it, I’d rather see evidence that the abstraction itself is common: for example, a corpus analysis of real PHP applications/frameworks showing how often this exact offset/length masking pattern occurs and what existing implementations look like. Otherwise we may just be benchmarking a convenience wrapper.

Also, the current RFC still documents out-of-bounds offsets as returning the original string unchanged, despite your reply saying it was changed to fail-closed, and it still says a multi-byte $mask_char is silently reduced to its first byte. Those semantics should probably be made consistent first.

And Jordi’s #[SensitiveParameter] point seems especially relevant if handling sensitive data is the primary motivation.

So I think the order should be: demonstrate the use-case, settle the contract, then benchmark the implementation.

Best regards,
Pratik Bhujel

در تاریخ شنبه ۱۹ سپتامبر ۲۰۲۶، ۱۵:۲۰ Pratik Bhujel <prateekbhujelpb@gmail.com> نوشت:

Hi Sepehr,

I don’t think benchmarks actually answer the main objection being raised here. They can show that a dedicated C implementation is faster than composing substr_replace() and str_repeat(), but they cannot show that this operation deserves a permanent core API.

Before optimizing it, I’d rather see evidence that the abstraction itself is common: for example, a corpus analysis of real PHP applications/frameworks showing how often this exact offset/length masking pattern occurs and what existing implementations look like. Otherwise we may just be benchmarking a convenience wrapper.

Also, the current RFC still documents out-of-bounds offsets as returning the original string unchanged, despite your reply saying it was changed to fail-closed, and it still says a multi-byte $mask_char is silently reduced to its first byte. Those semantics should probably be made consistent first.

And Jordi’s #[SensitiveParameter] point seems especially relevant if handling sensitive data is the primary motivation.

So I think the order should be: demonstrate the use-case, settle the contract, then benchmark the implementation.

Best regards,
Pratik Bhujel


Hi Pratik,

Thank you for your feedback and for taking the time to review my proposal.

You make some very valid points regarding optimization. I completely agree with you that performance within the PHP core is critical and must meet the highest standards.

My main motivation for this RFC is to address a practical need in real-world scenarios. When I see major frameworks like CakePHP having to implement custom utility functions for simple tasks like string masking, it suggests that we are reinventing the wheel. Standardizing this capability within the PHP core would be a significant benefit to the entire ecosystem.

I would be very happy to hear your specific thoughts on the implementation. If you have any suggestions on how I can improve the proposal or address your concerns, I am very open to that discussion.

The evidence for my point is available in my RFC.

Looking forward to hearing from you.

Best regards,
Sepehr

Hi Sepehr,

Thanks. I think we may be talking past each other slightly.

When I see major frameworks like CakePHP having to implement custom utility functions for simple tasks like string masking, it suggests that we are reinventing the wheel.

That shows that masking is a real use case, which I don’t dispute. What I’m still missing is evidence that this specific primitive is what those projects are repeatedly reinventing.

If CakePHP is part of the motivation, I think the strongest evidence would be to show an actual CakePHP implementation/use case that could be replaced by:

str_mask(string, mask_char, offset, length)

without changing its semantics. Even better would be a small prior-art section with several independent libraries/frameworks converging on roughly the same operation.

Otherwise, “frameworks implement masking” establishes the problem, but not necessarily this particular API as the abstraction PHP core should standardize.

The evidence for my point is available in my RFC.

I did read it. My concern is exactly the distinction above: evidence that masking exists is different from evidence that this API is the common missing primitive.

Assuming the recent semantic issues have now been addressed, this is the part I would focus on before implementation-level optimization. A concrete before/after from the cited real-world code would make the case much easier to evaluate.

Best regards,
Pratik Bhujel

On Sat, 19 Sep 2026 20:53:50 +0330, “سپهر محمودی” sepehrphpr@gmail.com wrote:

در تاریخ شنبه ۱۹ سپتامبر ۲۰۲۶، ۱۵:۲۰ Pratik Bhujel prateekbhujelpb@gmail.com نوشت:

Hi Sepehr,

I don’t think benchmarks actually answer the main objection being raised here. They can show that a dedicated C implementation is faster than composing substr_replace() and str_repeat(), but they cannot show that this operation deserves a permanent core API.

Before optimizing it, I’d rather see evidence that the abstraction itself is common: for example, a corpus analysis of real PHP applications/frameworks showing how often this exact offset/length masking pattern occurs and what existing implementations look like. Otherwise we may just be benchmarking a convenience wrapper.

Also, the current RFC still documents out-of-bounds offsets as returning the original string unchanged, despite your reply saying it was changed to fail-closed, and it still says a multi-byte $mask_char is silently reduced to its first byte. Those semantics should probably be made consistent first.

And Jordi’s #[SensitiveParameter] point seems especially relevant if handling sensitive data is the primary motivation.

So I think the order should be: demonstrate the use-case, settle the contract, then benchmark the implementation.

Best regards,

Pratik Bhujel

Hi Pratik,

Thank you for your feedback and for taking the time to review my proposal.

You make some very valid points regarding optimization. I completely agree with you that performance within the PHP core is critical and must meet the highest standards.

My main motivation for this RFC is to address a practical need in real-world scenarios. When I see major frameworks like CakePHP having to implement custom utility functions for simple tasks like string masking, it suggests that we are reinventing the wheel. Standardizing this capability within the PHP core would be a significant benefit to the entire ecosystem.

I would be very happy to hear your specific thoughts on the implementation. If you have any suggestions on how I can improve the proposal or address your concerns, I am very open to that discussion.

The evidence for my point is available in my RFC.

Looking forward to hearing from you.

Best regards,

Sepehr

در تاریخ یکشنبه ۲۰ سپتامبر ۲۰۲۶، ۰۴:۴۳ Pratik Bhujel <prateekbhujelpb@gmail.com> نوشت:

Hi Sepehr,

Thanks. I think we may be talking past each other slightly.

When I see major frameworks like CakePHP having to implement custom utility functions for simple tasks like string masking, it suggests that we are reinventing the wheel.

That shows that masking is a real use case, which I don’t dispute. What I’m still missing is evidence that this specific primitive is what those projects are repeatedly reinventing.

If CakePHP is part of the motivation, I think the strongest evidence would be to show an actual CakePHP implementation/use case that could be replaced by:

str_mask(string, mask_char, offset, length)

without changing its semantics. Even better would be a small prior-art section with several independent libraries/frameworks converging on roughly the same operation.

Otherwise, “frameworks implement masking” establishes the problem, but not necessarily this particular API as the abstraction PHP core should standardize.

The evidence for my point is available in my RFC.

I did read it. My concern is exactly the distinction above: evidence that masking exists is different from evidence that this API is the common missing primitive.

Assuming the recent semantic issues have now been addressed, this is the part I would focus on before implementation-level optimization. A concrete before/after from the cited real-world code would make the case much easier to evaluate.

Best regards,
Pratik Bhujel

On Sat, 19 Sep 2026 20:53:50 +0330, “سپهر محمودی” sepehrphpr@gmail.com wrote:

در تاریخ شنبه ۱۹ سپتامبر ۲۰۲۶، ۱۵:۲۰ Pratik Bhujel prateekbhujelpb@gmail.com نوشت:

Hi Sepehr,

I don’t think benchmarks actually answer the main objection being raised here. They can show that a dedicated C implementation is faster than composing substr_replace() and str_repeat(), but they cannot show that this operation deserves a permanent core API.

Before optimizing it, I’d rather see evidence that the abstraction itself is common: for example, a corpus analysis of real PHP applications/frameworks showing how often this exact offset/length masking pattern occurs and what existing implementations look like. Otherwise we may just be benchmarking a convenience wrapper.

Also, the current RFC still documents out-of-bounds offsets as returning the original string unchanged, despite your reply saying it was changed to fail-closed, and it still says a multi-byte $mask_char is silently reduced to its first byte. Those semantics should probably be made consistent first.

And Jordi’s #[SensitiveParameter] point seems especially relevant if handling sensitive data is the primary motivation.

So I think the order should be: demonstrate the use-case, settle the contract, then benchmark the implementation.

Best regards,

Pratik Bhujel

Hi Pratik,

Thank you for your feedback and for taking the time to review my proposal.

You make some very valid points regarding optimization. I completely agree with you that performance within the PHP core is critical and must meet the highest standards.

My main motivation for this RFC is to address a practical need in real-world scenarios. When I see major frameworks like CakePHP having to implement custom utility functions for simple tasks like string masking, it suggests that we are reinventing the wheel. Standardizing this capability within the PHP core would be a significant benefit to the entire ecosystem.

I would be very happy to hear your specific thoughts on the implementation. If you have any suggestions on how I can improve the proposal or address your concerns, I am very open to that discussion.

The evidence for my point is available in my RFC.

Looking forward to hearing from you.

Best regards,

Sepehr


Hi Pratik,

Fair point! I see what you mean now. You’re completely right that just showing “masking is needed” isn’t enough—I need to show why this specific (string, mask_char, offset, length) signature is the right primitive to standardize.

To answer that directly:

When people build custom masking logic today, almost everyone lands on composing substr_replace(), str_repeat(), and strlen():

// What people write today:
$masked = substr_replace(
$pan,
str_repeat(‘*’, $length ?? (strlen($pan) - $offset)),
$offset,
$length ?? (strlen($pan) - $offset)
);

// What str_mask replaces it with:
$masked = str_mask($pan, ‘*’, $offset, $length);

They end up using these exact parameters because offset and length are already the standard way PHP handles string slicing (like in substr and substr_replace).

Other frameworks and internal tools (like Laravel’s Str::mask) also converge on this exact signature because it’s the most natural fit for PHP’s existing conventions.

The big difference with making this a core primitive isn’t just saving a line of code—it’s getting fail-closed error handling (throwing ValueError instead of silently messing up or leaking data), built-in #[SensitiveParameter] protection, and avoiding temporary string allocations in memory.

I’ve updated the RFC with a quick Before/After comparison to make this clearer.

Appreciate the feedback, it really helped sharpen the focus!

Cheers,
Sepehr

در تاریخ یکشنبه ۲۰ سپتامبر ۲۰۲۶، ۱۳:۵۲ سپهر محمودی <sepehrphpr@gmail.com> نوشت:

در تاریخ یکشنبه ۲۰ سپتامبر ۲۰۲۶، ۰۴:۴۳ Pratik Bhujel <prateekbhujelpb@gmail.com> نوشت:

Hi Sepehr,

Thanks. I think we may be talking past each other slightly.

When I see major frameworks like CakePHP having to implement custom utility functions for simple tasks like string masking, it suggests that we are reinventing the wheel.

That shows that masking is a real use case, which I don’t dispute. What I’m still missing is evidence that this specific primitive is what those projects are repeatedly reinventing.

If CakePHP is part of the motivation, I think the strongest evidence would be to show an actual CakePHP implementation/use case that could be replaced by:

str_mask(string, mask_char, offset, length)

without changing its semantics. Even better would be a small prior-art section with several independent libraries/frameworks converging on roughly the same operation.

Otherwise, “frameworks implement masking” establishes the problem, but not necessarily this particular API as the abstraction PHP core should standardize.

The evidence for my point is available in my RFC.

I did read it. My concern is exactly the distinction above: evidence that masking exists is different from evidence that this API is the common missing primitive.

Assuming the recent semantic issues have now been addressed, this is the part I would focus on before implementation-level optimization. A concrete before/after from the cited real-world code would make the case much easier to evaluate.

Best regards,
Pratik Bhujel


Hey Pratik,

Regarding the evidence for common usage: I’ve actually included a direct comparison and link in the RFC’s “Motivation / Prior Art” section at the very end. It demonstrates how existing code patterns directly map to the proposed str_mask() signature.

Would be great if you could glance over it when you have a moment. Let me know if that addresses your point!

Best,
Sepehr

Hey dear.

(I sent to you an email on Private without realising, I’m sorry for the duplication).

It is an interesting proposal. Although I have some issue with the proposal.

I’m not an internal contributor, so my opinion doesn’t have anything to do with any RFC approval.

The str_mask function is something that, for sensitive data, is good for hiding. That’s something done with payment providers such as stripe.

However, there are many useful functions that, even though useful, aren’t included in an RFC. For example: a slugger/sluggify function. This is the most important component in CMS and template builders. For example: I’m creating a modular CMS as Symfony composer dependency. Routing of pages are dynamic, and require a page path that is a valid in a SEO perspective.

Would a slugify extension be useful? It would be as useful as a slugify composer package (to which point I use uniqid + a prefix for default slug, and then I just change it manually). I may create a C extension for that. However, does it need to be included inside a PHP internal? I don’t think so. No need to. I can create and external package for that, and Install in every machine or docker image of PHP+ my extension.

I don’t think business logic should be included in a programming language API. Furthermore, I think it should be as much atomic as possible so that other developers can build upon it. Look for PHP 8.5 with pipe operator “|>”: this is actually the greatest invention I have seen. It is a very useful pattern that can be used to rewrite many hard to read program into a function-like code. Even the str_mark can be created as a function through it, and it would be perfectly fine.

From what I read, you don’t need str_repeat . . . You can just use “****” of “XXXX” N times you need. That is something that is very dependent on a niche use case. I consider ecommerce niche, as not every single web developer (the purpose of PHP in the first place) are involved in ecommerce. It’s sounds reasonable that PHP is done for developers to get creative and create their program. That’s the reason libraries exist in the first place.

What about uniqid function, doesn’t it look like str_mark dilemma? I don’t think so: because creating unique IDs is something very necessary when you work with databases to ensure that every single object as a unique identifier. And not only that: I also use it for Server Side Rendering pages where an HTML component generated by PHP needs an autogenerated id tag (i.e: a carousel element with a unique identifier for JS code to handle it dynamically). And both use cases pass from niche to actually useful in any situation. The fact that you can add entropy is also an important supplement to the function. This logic also applies to cryptographic functions, maths function, data structures, SPL, and others. Because it is not only about use case (which is crucial), it is also about the difficulty to implement one from scratch, or use a PHP library for it.

Also, if Cake PHP needed an extension for it, I think they would first create a composer PHP package, else an extension to it. But creating an extension to avoid 2-3 function calls sounds excessive, knowing that not every single project needs to handle PII data (name, SSN, Credit Cards, etc). Also, when you handle Credit Cards, from what I know, most companies uses an external a payment provider that mask this information by default (maybe due to PII norms). So the use for credit card goes automatically discarded (unless you are a PII compliant payment provider, to which you still don’t need an extension for a function that a PHP class can provide as a method, in case of Object Mapping classes).

So, I think it would be quicker and more productive for you to start as an individual extension, get it very well known, and then by the approval of several people, get it included later in a PHP RFC. Because I have seen many small packages and extensions that started as an independent project, and then included in the main API.

I also say it because honestly, it is very tiresome to open the mail Inbox, and read a conversation that goes near 15 threads, in which the starter of the thread goes back and forth explaining his point with somehow vague objectives, with some contradictions and stating facts that were not mentioned at the first place.

I’m not going against you. It is just that it is not convincing enough to add it into a main API of a PL. And I’m not saying that it is not useful, it is just that this can be done easily in many different ways. But I see your enthusiasm, and I think it would definitely go well if you created, let’s say, an organisation that create useful extension outside the main API. Because adding such a small function doesn’t look useful in a PHP8.* release page neither.

I don’t want to be rude, I’m sorry if it sounds like it. It just looks like the conversation about the RFC is going nowhere.

Thanks for your attention,
David Maye

El 20/09/2026 13:02 سپهر محمودی sepehrphpr@gmail.com escribió:

در تاریخ یکشنبه ۲۰ سپتامبر ۲۰۲۶، ۱۳:۵۲ سپهر محمودی <sepehrphpr@gmail.com> نوشت:

در تاریخ یکشنبه ۲۰ سپتامبر ۲۰۲۶، ۰۴:۴۳ Pratik Bhujel <prateekbhujelpb@gmail.com> نوشت:

Hi Sepehr,

Thanks. I think we may be talking past each other slightly.

When I see major frameworks like CakePHP having to implement custom utility functions for simple tasks like string masking, it suggests that we are reinventing the wheel.

That shows that masking is a real use case, which I don’t dispute. What I’m still missing is evidence that this specific primitive is what those projects are repeatedly reinventing.

If CakePHP is part of the motivation, I think the strongest evidence would be to show an actual CakePHP implementation/use case that could be replaced by:

str_mask(string, mask_char, offset, length)

without changing its semantics. Even better would be a small prior-art section with several independent libraries/frameworks converging on roughly the same operation.

Otherwise, “frameworks implement masking” establishes the problem, but not necessarily this particular API as the abstraction PHP core should standardize.

The evidence for my point is available in my RFC.

I did read it. My concern is exactly the distinction above: evidence that masking exists is different from evidence that this API is the common missing primitive.

Assuming the recent semantic issues have now been addressed, this is the part I would focus on before implementation-level optimization. A concrete before/after from the cited real-world code would make the case much easier to evaluate.

Best regards,
Pratik Bhujel


Hey Pratik,

Regarding the evidence for common usage: I’ve actually included a direct comparison and link in the RFC’s “Motivation / Prior Art” section at the very end. It demonstrates how existing code patterns directly map to the proposed str_mask() signature.

Would be great if you could glance over it when you have a moment. Let me know if that addresses your point!

Best,
Sepehr

Hi Sepehr, David,

I checked the updated prior art, and I want to correct one thing from
my earlier objection: Laravel's Str::mask genuinely does converge on
the character/index/length shape (with an encoding parameter), and
CakePHP 5.4 now has Text::mask with essentially the same core
operation. So I don't think it would be fair to say there is no prior
art here.

But that actually makes the remaining question sharper: what exactly
are we standardizing?

Both Laravel and CakePHP are multibyte-aware, and both have behavior
that differs from the RFC's new fail-closed semantics. For example,
they can return the original string for ranges that do not select
anything, whereas the RFC now proposes ValueError for invalid
boundaries. If prior art is part of the justification, then PHP core
would not simply be standardizing that prior art; it would be
deliberately changing its contract. That may be the right choice, but
I think it needs to be justified separately.

Also, #[SensitiveParameter] is useful, but it isn't a core-only
benefit. Userland functions can already annotate sensitive parameters
with it, so I don't think that by itself strengthens the case for
putting the operation in core.

There is one other point I genuinely need clarified. Earlier in this
thread you wrote that you had not designed or implemented
grapheme_mask() yet. But there is already a Grapheme Mask RFC from
June under your name, currently marked "Under Discussion", with the
same offset/length model and an explicit note that str_mask() would
follow as a separate RFC. Did you mean that you have not implemented a
revised version? As written, those two statements do not line up.

So at this point I think the disagreement is fairly clear. There is
real prior art for a masking helper, which answers part of my earlier
question. What is still open is whether that helper belongs in core,
and whether the proposed semantics are the right ones when they
intentionally differ from the frameworks being cited as prior art.

I don't think another benchmark round resolves that API-design question.

I've said my piece, so unless the semantics or rationale changes
materially, I'll leave it there and let the rest of internals decide.

Best regards,
Pratik Bhujel

On Sun, 20 Sep 2026 15:08:53 +0200 (CEST), David Maye Kitenge
<david.maye@seigisoft.com> wrote:

Hey dear.

(I sent to you an email on Private without realising, I'm sorry for the duplication).

It is an interesting proposal. Although I have some issue with the proposal.

I'm not an internal contributor, so my opinion doesn't have anything to do with any RFC approval.

The str_mask function is something that, for sensitive data, is good for hiding. That's something done with payment providers such as stripe.

However, there are many useful functions that, even though useful, aren't included in an RFC. For example: a slugger/sluggify function. This is the most important component in CMS and template builders. For example: I'm creating a modular CMS as Symfony composer dependency. Routing of pages are dynamic, and require a page path that is a valid in a SEO perspective.

Would a slugify extension be useful? It would be as useful as a slugify composer package (to which point I use `uniqid` + a prefix for default slug, and then I just change it manually). I may create a C extension for that. However, does it need to be included inside a PHP internal? I don't think so. No need to. I can create and external package for that, and Install in every machine or docker image of PHP+ my extension.

I don't think business logic should be included in a programming language API. Furthermore, I think it should be as much atomic as possible so that other developers can build upon it. Look for PHP 8.5 with pipe operator "|>": this is actually the greatest invention I have seen. It is a very useful pattern that can be used to rewrite many hard to read program into a function-like code. Even the str_mark can be created as a function through it, and it would be perfectly fine.

From what I read, you don't need str_repeat . . . You can just use "****" of "XXXX" N times you need. That is something that is very dependent on a niche use case. I consider ecommerce niche, as not every single web developer (the purpose of PHP in the first place) are involved in ecommerce. It's sounds reasonable that PHP is done for developers to get creative and create their program. That's the reason libraries exist in the first place.

What about `uniqid` function, doesn't it look like `str_mark` dilemma? I don't think so: because creating unique IDs is something very necessary when you work with databases to ensure that every single object as a unique identifier. And not only that: I also use it for Server Side Rendering pages where an HTML component generated by PHP needs an autogenerated id tag (i.e: a carousel element with a unique identifier for JS code to handle it dynamically). And both use cases pass from niche to actually useful in any situation. The fact that you can add entropy is also an important supplement to the function. This logic also applies to cryptographic functions, maths function, data structures, SPL, and others. Because it is not only about use case (which is crucial), it is also about the difficulty to implement one from scratch, or use a PHP library for it.

Also, if Cake PHP needed an extension for it, I think they would first create a composer PHP package, else an extension to it. But creating an extension to avoid 2-3 function calls sounds excessive, knowing that not every single project needs to handle PII data (name, SSN, Credit Cards, etc). Also, when you handle Credit Cards, from what I know, most companies uses an external a payment provider that mask this information by default (maybe due to PII norms). So the use for credit card goes automatically discarded (unless you are a PII compliant payment provider, to which you still don't need an extension for a function that a PHP class can provide as a method, in case of Object Mapping classes).

So, I think it would be quicker and more productive for you to start as an individual extension, get it very well known, and then by the approval of several people, get it included later in a PHP RFC. Because I have seen many small packages and extensions that started as an independent project, and then included in the main API.

I also say it because honestly, it is very tiresome to open the mail Inbox, and read a conversation that goes near 15 threads, in which the starter of the thread goes back and forth explaining his point with somehow vague objectives, with some contradictions and stating facts that were not mentioned at the first place.

I'm not going against you. It is just that it is not convincing enough to add it into a main API of a PL. And I'm not saying that it is not useful, it is just that this can be done easily in many different ways. But I see your enthusiasm, and I think it would definitely go well if you created, let's say, an organisation that create useful extension outside the main API. Because adding such a small function doesn't look useful in a PHP8.* release page neither.

I don't want to be rude, I'm sorry if it sounds like it. It just looks like the conversation about the RFC is going nowhere.

Thanks for your attention,

David Maye

El 20/09/2026 13:02 سپهر محمودی <sepehrphpr@gmail.com> escribió:

در تاریخ یکشنبه ۲۰ سپتامبر ۲۰۲۶، ۱۳:۵۲ سپهر محمودی <sepehrphpr@gmail.com> نوشت:

در تاریخ یکشنبه ۲۰ سپتامبر ۲۰۲۶، ۰۴:۴۳ Pratik Bhujel <prateekbhujelpb@gmail.com> نوشت:

Hi Sepehr,

Thanks. I think we may be talking past each other slightly.

When I see major frameworks like CakePHP having to implement custom utility functions for simple tasks like string masking, it suggests that we are reinventing the wheel.

That shows that masking is a real use case, which I don’t dispute. What I’m still missing is evidence that this specific primitive is what those projects are repeatedly reinventing.

If CakePHP is part of the motivation, I think the strongest evidence would be to show an actual CakePHP implementation/use case that could be replaced by:

str_mask(string, mask_char, offset, length)

without changing its semantics. Even better would be a small prior-art section with several independent libraries/frameworks converging on roughly the same operation.

Otherwise, “frameworks implement masking” establishes the problem, but not necessarily this particular API as the abstraction PHP core should standardize.

The evidence for my point is available in my RFC.

I did read it. My concern is exactly the distinction above: evidence that masking exists is different from evidence that this API is the common missing primitive.

Assuming the recent semantic issues have now been addressed, this is the part I would focus on before implementation-level optimization. A concrete before/after from the cited real-world code would make the case much easier to evaluate.

Best regards,
Pratik Bhujel

----------

Hey Pratik,

Regarding the evidence for common usage: I've actually included a direct comparison and link in the RFC's "Motivation / Prior Art" section at the very end. It demonstrates how existing code patterns directly map to the proposed `str_mask()` signature.

Would be great if you could glance over it when you have a moment. Let me know if that addresses your point!

Best,

Sepehr

در تاریخ یکشنبه ۲۰ سپتامبر ۲۰۲۶، ۱۶:۳۸ David Maye Kitenge <david.maye@seigisoft.com> نوشت:

Hey dear.

(I sent to you an email on Private without realising, I’m sorry for the duplication).

It is an interesting proposal. Although I have some issue with the proposal.

I’m not an internal contributor, so my opinion doesn’t have anything to do with any RFC approval.

The str_mask function is something that, for sensitive data, is good for hiding. That’s something done with payment providers such as stripe.

However, there are many useful functions that, even though useful, aren’t included in an RFC. For example: a slugger/sluggify function. This is the most important component in CMS and template builders. For example: I’m creating a modular CMS as Symfony composer dependency. Routing of pages are dynamic, and require a page path that is a valid in a SEO perspective.

Would a slugify extension be useful? It would be as useful as a slugify composer package (to which point I use uniqid + a prefix for default slug, and then I just change it manually). I may create a C extension for that. However, does it need to be included inside a PHP internal? I don’t think so. No need to. I can create and external package for that, and Install in every machine or docker image of PHP+ my extension.

I don’t think business logic should be included in a programming language API. Furthermore, I think it should be as much atomic as possible so that other developers can build upon it. Look for PHP 8.5 with pipe operator “|>”: this is actually the greatest invention I have seen. It is a very useful pattern that can be used to rewrite many hard to read program into a function-like code. Even the str_mark can be created as a function through it, and it would be perfectly fine.

From what I read, you don’t need str_repeat . . . You can just use “****” of “XXXX” N times you need. That is something that is very dependent on a niche use case. I consider ecommerce niche, as not every single web developer (the purpose of PHP in the first place) are involved in ecommerce. It’s sounds reasonable that PHP is done for developers to get creative and create their program. That’s the reason libraries exist in the first place.

What about uniqid function, doesn’t it look like str_mark dilemma? I don’t think so: because creating unique IDs is something very necessary when you work with databases to ensure that every single object as a unique identifier. And not only that: I also use it for Server Side Rendering pages where an HTML component generated by PHP needs an autogenerated id tag (i.e: a carousel element with a unique identifier for JS code to handle it dynamically). And both use cases pass from niche to actually useful in any situation. The fact that you can add entropy is also an important supplement to the function. This logic also applies to cryptographic functions, maths function, data structures, SPL, and others. Because it is not only about use case (which is crucial), it is also about the difficulty to implement one from scratch, or use a PHP library for it.

Also, if Cake PHP needed an extension for it, I think they would first create a composer PHP package, else an extension to it. But creating an extension to avoid 2-3 function calls sounds excessive, knowing that not every single project needs to handle PII data (name, SSN, Credit Cards, etc). Also, when you handle Credit Cards, from what I know, most companies uses an external a payment provider that mask this information by default (maybe due to PII norms). So the use for credit card goes automatically discarded (unless you are a PII compliant payment provider, to which you still don’t need an extension for a function that a PHP class can provide as a method, in case of Object Mapping classes).

So, I think it would be quicker and more productive for you to start as an individual extension, get it very well known, and then by the approval of several people, get it included later in a PHP RFC. Because I have seen many small packages and extensions that started as an independent project, and then included in the main API.

I also say it because honestly, it is very tiresome to open the mail Inbox, and read a conversation that goes near 15 threads, in which the starter of the thread goes back and forth explaining his point with somehow vague objectives, with some contradictions and stating facts that were not mentioned at the first place.

I’m not going against you. It is just that it is not convincing enough to add it into a main API of a PL. And I’m not saying that it is not useful, it is just that this can be done easily in many different ways. But I see your enthusiasm, and I think it would definitely go well if you created, let’s say, an organisation that create useful extension outside the main API. Because adding such a small function doesn’t look useful in a PHP8.* release page neither.

I don’t want to be rude, I’m sorry if it sounds like it. It just looks like the conversation about the RFC is going nowhere.

Thanks for your attention,
David Maye

El 20/09/2026 13:02 سپهر محمودی <sepehrphpr@gmail.com> escribió:

در تاریخ یکشنبه ۲۰ سپتامبر ۲۰۲۶، ۱۳:۵۲ سپهر محمودی <sepehrphpr@gmail.com> نوشت:

در تاریخ یکشنبه ۲۰ سپتامبر ۲۰۲۶، ۰۴:۴۳ Pratik Bhujel <prateekbhujelpb@gmail.com> نوشت:

Hi Sepehr,

Thanks. I think we may be talking past each other slightly.

When I see major frameworks like CakePHP having to implement custom utility functions for simple tasks like string masking, it suggests that we are reinventing the wheel.

That shows that masking is a real use case, which I don’t dispute. What I’m still missing is evidence that this specific primitive is what those projects are repeatedly reinventing.

If CakePHP is part of the motivation, I think the strongest evidence would be to show an actual CakePHP implementation/use case that could be replaced by:

str_mask(string, mask_char, offset, length)

without changing its semantics. Even better would be a small prior-art section with several independent libraries/frameworks converging on roughly the same operation.

Otherwise, “frameworks implement masking” establishes the problem, but not necessarily this particular API as the abstraction PHP core should standardize.

The evidence for my point is available in my RFC.

I did read it. My concern is exactly the distinction above: evidence that masking exists is different from evidence that this API is the common missing primitive.

Assuming the recent semantic issues have now been addressed, this is the part I would focus on before implementation-level optimization. A concrete before/after from the cited real-world code would make the case much easier to evaluate.

Best regards,
Pratik Bhujel


Hey Pratik,

Regarding the evidence for common usage: I’ve actually included a direct comparison and link in the RFC’s “Motivation / Prior Art” section at the very end. It demonstrates how existing code patterns directly map to the proposed str_mask() signature.

Would be great if you could glance over it when you have a moment. Let me know if that addresses your point!

Best,
Sepehr


Hi David,

Thanks for sharing your detailed thoughts and perspective!

Just to clarify the intent: str_mask() is designed not as domain/business logic (like slugification), but as a low-level string manipulation primitive—very similar to existing core primitives like str_pad(), substr_replace(), and str_repeat().

The main reasons for considering it at the core level rather than userland/PECL are:

  1. First-class integration with #[\SensitiveParameter] to ensure safe handling of sensitive/PII data in stack traces.
  2. Providing a strict, fail-closed contract natively without the allocation and performance overhead of composing multiple userland calls.

That said, I appreciate your feedback and constructive criticism regarding the mailing list discussion. I’ve updated the RFC with the latest technical points and benchmarks, and I’ll let the RFC process and core voters decide on its inclusion.

Thanks again for your time!

Best regards,
Sepehr

در تاریخ یکشنبه ۲۰ سپتامبر ۲۰۲۶، ۱۷:۰۵ Pratik Bhujel <prateekbhujelpb@gmail.com> نوشت:

Hi Sepehr, David,

I checked the updated prior art, and I want to correct one thing from
my earlier objection: Laravel’s Str::mask genuinely does converge on
the character/index/length shape (with an encoding parameter), and
CakePHP 5.4 now has Text::mask with essentially the same core
operation. So I don’t think it would be fair to say there is no prior
art here.

But that actually makes the remaining question sharper: what exactly
are we standardizing?

Both Laravel and CakePHP are multibyte-aware, and both have behavior
that differs from the RFC’s new fail-closed semantics. For example,
they can return the original string for ranges that do not select
anything, whereas the RFC now proposes ValueError for invalid
boundaries. If prior art is part of the justification, then PHP core
would not simply be standardizing that prior art; it would be
deliberately changing its contract. That may be the right choice, but
I think it needs to be justified separately.

Also, #[SensitiveParameter] is useful, but it isn’t a core-only
benefit. Userland functions can already annotate sensitive parameters
with it, so I don’t think that by itself strengthens the case for
putting the operation in core.

There is one other point I genuinely need clarified. Earlier in this
thread you wrote that you had not designed or implemented
grapheme_mask() yet. But there is already a Grapheme Mask RFC from
June under your name, currently marked “Under Discussion”, with the
same offset/length model and an explicit note that str_mask() would
follow as a separate RFC. Did you mean that you have not implemented a
revised version? As written, those two statements do not line up.

So at this point I think the disagreement is fairly clear. There is
real prior art for a masking helper, which answers part of my earlier
question. What is still open is whether that helper belongs in core,
and whether the proposed semantics are the right ones when they
intentionally differ from the frameworks being cited as prior art.

I don’t think another benchmark round resolves that API-design question.

I’ve said my piece, so unless the semantics or rationale changes
materially, I’ll leave it there and let the rest of internals decide.

Best regards,
Pratik Bhujel

On Sun, 20 Sep 2026 15:08:53 +0200 (CEST), David Maye Kitenge
<david.maye@seigisoft.com> wrote:

Hey dear.

(I sent to you an email on Private without realising, I’m sorry for the duplication).

It is an interesting proposal. Although I have some issue with the proposal.

I’m not an internal contributor, so my opinion doesn’t have anything to do with any RFC approval.

The str_mask function is something that, for sensitive data, is good for hiding. That’s something done with payment providers such as stripe.

However, there are many useful functions that, even though useful, aren’t included in an RFC. For example: a slugger/sluggify function. This is the most important component in CMS and template builders. For example: I’m creating a modular CMS as Symfony composer dependency. Routing of pages are dynamic, and require a page path that is a valid in a SEO perspective.

Would a slugify extension be useful? It would be as useful as a slugify composer package (to which point I use uniqid + a prefix for default slug, and then I just change it manually). I may create a C extension for that. However, does it need to be included inside a PHP internal? I don’t think so. No need to. I can create and external package for that, and Install in every machine or docker image of PHP+ my extension.

I don’t think business logic should be included in a programming language API. Furthermore, I think it should be as much atomic as possible so that other developers can build upon it. Look for PHP 8.5 with pipe operator “|>”: this is actually the greatest invention I have seen. It is a very useful pattern that can be used to rewrite many hard to read program into a function-like code. Even the str_mark can be created as a function through it, and it would be perfectly fine.

From what I read, you don’t need str_repeat . . . You can just use “****” of “XXXX” N times you need. That is something that is very dependent on a niche use case. I consider ecommerce niche, as not every single web developer (the purpose of PHP in the first place) are involved in ecommerce. It’s sounds reasonable that PHP is done for developers to get creative and create their program. That’s the reason libraries exist in the first place.

What about uniqid function, doesn’t it look like str_mark dilemma? I don’t think so: because creating unique IDs is something very necessary when you work with databases to ensure that every single object as a unique identifier. And not only that: I also use it for Server Side Rendering pages where an HTML component generated by PHP needs an autogenerated id tag (i.e: a carousel element with a unique identifier for JS code to handle it dynamically). And both use cases pass from niche to actually useful in any situation. The fact that you can add entropy is also an important supplement to the function. This logic also applies to cryptographic functions, maths function, data structures, SPL, and others. Because it is not only about use case (which is crucial), it is also about the difficulty to implement one from scratch, or use a PHP library for it.

Also, if Cake PHP needed an extension for it, I think they would first create a composer PHP package, else an extension to it. But creating an extension to avoid 2-3 function calls sounds excessive, knowing that not every single project needs to handle PII data (name, SSN, Credit Cards, etc). Also, when you handle Credit Cards, from what I know, most companies uses an external a payment provider that mask this information by default (maybe due to PII norms). So the use for credit card goes automatically discarded (unless you are a PII compliant payment provider, to which you still don’t need an extension for a function that a PHP class can provide as a method, in case of Object Mapping classes).

So, I think it would be quicker and more productive for you to start as an individual extension, get it very well known, and then by the approval of several people, get it included later in a PHP RFC. Because I have seen many small packages and extensions that started as an independent project, and then included in the main API.

I also say it because honestly, it is very tiresome to open the mail Inbox, and read a conversation that goes near 15 threads, in which the starter of the thread goes back and forth explaining his point with somehow vague objectives, with some contradictions and stating facts that were not mentioned at the first place.

I’m not going against you. It is just that it is not convincing enough to add it into a main API of a PL. And I’m not saying that it is not useful, it is just that this can be done easily in many different ways. But I see your enthusiasm, and I think it would definitely go well if you created, let’s say, an organisation that create useful extension outside the main API. Because adding such a small function doesn’t look useful in a PHP8.* release page neither.

I don’t want to be rude, I’m sorry if it sounds like it. It just looks like the conversation about the RFC is going nowhere.

Thanks for your attention,

David Maye

El 20/09/2026 13:02 سپهر محمودی <sepehrphpr@gmail.com> escribió:

در تاریخ یکشنبه ۲۰ سپتامبر ۲۰۲۶، ۱۳:۵۲ سپهر محمودی <sepehrphpr@gmail.com> نوشت:

در تاریخ یکشنبه ۲۰ سپتامبر ۲۰۲۶، ۰۴:۴۳ Pratik Bhujel <prateekbhujelpb@gmail.com> نوشت:

Hi Sepehr,

Thanks. I think we may be talking past each other slightly.

When I see major frameworks like CakePHP having to implement custom utility functions for simple tasks like string masking, it suggests that we are reinventing the wheel.

That shows that masking is a real use case, which I don’t dispute. What I’m still missing is evidence that this specific primitive is what those projects are repeatedly reinventing.

If CakePHP is part of the motivation, I think the strongest evidence would be to show an actual CakePHP implementation/use case that could be replaced by:

str_mask(string, mask_char, offset, length)

without changing its semantics. Even better would be a small prior-art section with several independent libraries/frameworks converging on roughly the same operation.

Otherwise, “frameworks implement masking” establishes the problem, but not necessarily this particular API as the abstraction PHP core should standardize.

The evidence for my point is available in my RFC.

I did read it. My concern is exactly the distinction above: evidence that masking exists is different from evidence that this API is the common missing primitive.

Assuming the recent semantic issues have now been addressed, this is the part I would focus on before implementation-level optimization. A concrete before/after from the cited real-world code would make the case much easier to evaluate.

Best regards,
Pratik Bhujel


Hey Pratik,

Regarding the evidence for common usage: I’ve actually included a direct comparison and link in the RFC’s “Motivation / Prior Art” section at the very end. It demonstrates how existing code patterns directly map to the proposed str_mask() signature.

Would be great if you could glance over it when you have a moment. Let me know if that addresses your point!

Best,

Sepehr


Hi Pratik,

Thank you for the fair follow-up and for acknowledging the prior art examples.

To briefly address your points:

  1. Semantics & Fail-Closed:
    While frameworks historically return the original string on out-of-bounds inputs, PHP 8+ core standard library functions consistently throw ValueError for invalid offsets/lengths (e.g., substr()). For sensitive operations like masking, throwing a ValueError ensures a strict fail-closed contract—preventing silent fallback where unmasked data might inadvertently pass through unnoticed.

  2. Clarification on grapheme_mask():
    To clarify: the RFC draft for grapheme_mask() was created in June as a design concept, but the revised C implementation / PR matching the updated fail-closed semantics has not been completed yet. I apologize for any confusion in phrasing earlier.

I appreciate all your constructive feedback throughout this discussion. It has helped clarify the scope and trade-offs of the proposal. I agree we can leave the decision to internals.

Best regards,
Sepehr

On 2026-09-21 03:48, سپهر محمودی wrote:

Just to clarify the intent: `str_mask()` is designed not as domain/ business logic (like slugification), but as a low-level string manipulation primitive—very similar to existing core primitives like `str_pad()`, `substr_replace()`, and `str_repeat()`.

The main reasons for considering it at the core level rather than userland/PECL are:
1. First-class integration with `#[\SensitiveParameter]` to ensure safe handling of sensitive/PII data in stack traces.
2. Providing a strict, fail-closed contract natively without the allocation and performance overhead of composing multiple userland calls.

See, this #[\SensitiveParameter] thing is what makes it look to me like business logic rather than low-level; it assumes that what is being masked is - well - sensitive, according to some criterion. There might be other reasons why someone will want to mask part of a string that _isn't_ sensitive (anyone for a game of Hangman?).

It's reasonable to apply #[\SensitiveParameter] to the $password field of openssl_password_hash() or PDO::connect() because you are by definition passing a sensitive value (a password) in that parameter. But for low-level string manipulation? Strings are arbitrary sequences of bytes and "sensitivity" is not an inherent property of such things.

Morgan