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
-
Masking a credit card (positive offset & length):
$credit_card = '1234567890123456';
$masked_card = str_mask($credit_card, '*', 4, 8);
// Output: 1234********3456 -
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:
-
What recurring capability does str_mask() provide that cannot already be
expressed clearly with existing string primitives? -
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? -
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
در تاریخ جمعه ۱۸ سپتامبر ۲۰۲۶، ۲۲:۲۳ 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()andstrlen()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:
What recurring capability does str_mask() provide that cannot already
be expressed clearly with existing string primitives?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?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
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): stringExamples
Masking a credit card (positive offset & length):
$credit_card = '1234567890123456';
$masked_card = str_mask($credit_card, '*', 4, 8);
// Output: 1234********3456Masking a phone number (negative offset to count from the end):
$phone_number = '+989123456789';
$masked_phone = str_mask($phone_number, 'X', -4);
// Output: +9891234XXXXYou can find all the details, implementation plans, and RFC discussions here:
https://wiki.php.net/rfc/str_maskLooking 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
در تاریخ جمعه ۱۸ سپتامبر ۲۰۲۶، ۲۳:۱۲ 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): stringExamples
Masking a credit card (positive offset & length):
$credit_card = '1234567890123456';
$masked_card = str_mask($credit_card, '*', 4, 8);
// Output: 1234********3456Masking a phone number (negative offset to count from the end):
$phone_number = '+989123456789';
$masked_phone = str_mask($phone_number, 'X', -4);
// Output: +9891234XXXXYou can find all the details, implementation plans, and RFC discussions
here:
https://wiki.php.net/rfc/str_maskLooking forward to hearing your feedback and thoughts!
Best regards,
Sepehr MahmoudiHi,
Unless I am missing something...this looks identical to the existing
substr_replace. It has the same signature with just a different name
and withoutarraybeing 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():
-
Ergonomics & Clarity: Masking sensitive information (PII, credit cards,
emails, tokens) is one of the most common everyday tasks in modern web
security and logging. Writingsubstr_replace(..., str_repeat(...), ...)
is verbose and prone to off-by-one errors. A dedicated function makes the
intent clear. -
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 (throwingValueErroron invalid boundaries
or empty mask characters) to ensure sensitive data is never silently
exposed due to silent clipping. -
Performance & Memory: The
substr_replace + str_repeatcombination
performs two separate string allocations (one temporary string created by
str_repeatand the final string created bysubstr_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
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.
> 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.
Can you provide an actual example of out-of-bounds offsets and it's impact for us to understand?
> 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.
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
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
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.
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
در تاریخ شنبه ۱۹ سپتامبر ۲۰۲۶، ۰۶:۴۸ Jordi Kroon jordikroon@me.com نوشت:
On 18 Sep 2026 at 18:43 -0400, Casper Langemeijer langemeijer@php.net,
wrote: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.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 Jordi,
Thank you for the review. That is a very good point — I have added the
#[\SensitiveParameter] attribute to the string parameter so masked values
will no longer appear in backtraces. The change is pushed to the PR.
I understand the use-case may look niche, but I will share benchmark data
in the coming days that addresses the performance aspects raised in this
thread.
Best regards,
Sepehr
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
composingsubstr_replace()andstr_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 BhujelOn 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
composingsubstr_replace()andstr_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 mailto:sepehrphpr@gmail.com> نوشت:
در تاریخ یکشنبه ۲۰ سپتامبر ۲۰۲۶، ۰۴:۴۳ Pratik Bhujel <prateekbhujelpb@gmail.com mailto: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
uniqidfunction, doesn't it look likestr_markdilemma? 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
در تاریخ یکشنبه ۲۰ سپتامبر ۲۰۲۶، ۱۷:۰۵ 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 BhujelOn 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 useuniqid+ 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
uniqidfunction, doesn't it look likestr_markdilemma? 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
proposedstr_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:
-
Semantics & Fail-Closed:
While frameworks historically return the original string on
out-of-bounds inputs, PHP 8+ core standard library functions consistently
throwValueErrorfor invalid offsets/lengths (e.g.,substr()). For
sensitive operations like masking, throwing aValueErrorensures a strict
fail-closed contract—preventing silent fallback where unmasked data might
inadvertently pass through unnoticed. -
Clarification on
grapheme_mask():
To clarify: the RFC draft forgrapheme_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
در تاریخ یکشنبه ۲۰ سپتامبر ۲۰۲۶، ۱۶:۳۸ 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 useuniqid+ 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
uniqidfunction, doesn't it look likestr_markdilemma? 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 MayeEl 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
proposedstr_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:
- First-class integration with
#[\SensitiveParameter]to ensure safe
handling of sensitive/PII data in stack traces. - 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
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(), andstr_repeat().The main reasons for considering it at the core level rather than
userland/PECL are:
- First-class integration with
#[\SensitiveParameter]to ensure safe
handling of sensitive/PII data in stack traces.- 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
در تاریخ دوشنبه ۲۱ سپتامبر ۲۰۲۶، ۰۴:۰۴ Morgan weedpacket@varteg.nz نوشت:
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(), andstr_repeat().The main reasons for considering it at the core level rather than
userland/PECL are:
- First-class integration with
#[\SensitiveParameter]to ensure safe
handling of sensitive/PII data in stack traces.- 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
Hi Morgan,
That is a very valid and fair point.
The intention was to provide a helpful security default, but you are right:
str_mask() is fundamentally a low-level string manipulation primitive,
and masking is also used in non-sensitive contexts (like word games,
anonymized identifiers, or formatting). Treating it as a generic string
primitive without baking in domain-specific assumptions makes total sense.
I will remove the #[\SensitiveParameter] attribute from the RFC to keep
the function purely general-purpose, consistent with other ext/standard
string functions.
Thanks for the insightful feedback!
Best regards,
Sepehr
Hello dear Sepher.
So I have a question:
The main reasons for considering it at the core level rather than userland/PECL are:
- First-class integration with
#[\SensitiveParameter]to ensure safe handling of sensitive/PII data in stack traces
Wasn't the objective of the RFC to have first class integration with #[\SensitiveParameter]?
Also as you stated in that sentence, you want to injure handling of sensitive pii data in stack trades. However that's kind of uses very uses as a very specific use case. But was he RFC first design for that kind of use case?
Because it is kind of hard to understand it, the reason of the RFC, if in the same mails you sent also you state that you are rectifying the RFC. It sounds like the RFC first is designed for a very specific kind of use, which is very niche, and apart from that it is a very dynamically designed RFC.
It just I'm the kind of lost of why is the using in the first place. If you can do it in user land perfectly. And in the case of using c, you have extensions that are not part of the main API but solves a very important problem and issue. Why is the need in the main PHP API? Because it sounds like you're trying just to push something into the main API just for the sake of pushing it, and from each Mail the necessity of using it or it's consideration at least gradually decreases
Kind regards,
David Maye > El 21/09/2026 11:56 سپهر محمودی sepehrphpr@gmail.com escribió:
در تاریخ دوشنبه ۲۱ سپتامبر ۲۰۲۶، ۰۴:۰۴ Morgan <weedpacket@varteg.nz mailto:weedpacket@varteg.nz> نوشت:
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(), andstr_repeat().The main reasons for considering it at the core level rather than
userland/PECL are:
- First-class integration with
#[\SensitiveParameter]to ensure safe
handling of sensitive/PII data in stack traces.- 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
Hi Morgan,
That is a very valid and fair point.
The intention was to provide a helpful security default, but you are right:
str_mask()is fundamentally a low-level string manipulation primitive, and masking is also used in non-sensitive contexts (like word games, anonymized identifiers, or formatting). Treating it as a generic string primitive without baking in domain-specific assumptions makes total sense.I will remove the
#[\SensitiveParameter]attribute from the RFC to keep the function purely general-purpose, consistent with otherext/standardstring functions.Thanks for the insightful feedback!
Best regards,
Sepehr
Sorry from the last mail I meant Ensure, not injure. Grammatical character is on 😅. Sorry everyone > El 21/09/2026 12:15 David Maye david.maye@seigisoft.com escribió:
Hello dear Sepher.
So I have a question:
The main reasons for considering it at the core level rather than userland/PECL are:
- First-class integration with
#[\SensitiveParameter]to ensure safe handling of sensitive/PII data in stack tracesWasn't the objective of the RFC to have first class integration with
#[\SensitiveParameter]?Also as you stated in that sentence, you want to injure handling of sensitive pii data in stack trades. However that's kind of uses very uses as a very specific use case. But was he RFC first design for that kind of use case?
Because it is kind of hard to understand it, the reason of the RFC, if in the same mails you sent also you state that you are rectifying the RFC. It sounds like the RFC first is designed for a very specific kind of use, which is very niche, and apart from that it is a very dynamically designed RFC.
It just I'm the kind of lost of why is the using in the first place. If you can do it in user land perfectly. And in the case of using c, you have extensions that are not part of the main API but solves a very important problem and issue. Why is the need in the main PHP API? Because it sounds like you're trying just to push something into the main API just for the sake of pushing it, and from each Mail the necessity of using it or it's consideration at least gradually decreases
Kind regards,
David Maye > > El 21/09/2026 11:56 سپهر محمودی sepehrphpr@gmail.com escribió:
در تاریخ دوشنبه ۲۱ سپتامبر ۲۰۲۶، ۰۴:۰۴ Morgan <weedpacket@varteg.nz mailto:weedpacket@varteg.nz> نوشت:
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(), andstr_repeat().The main reasons for considering it at the core level rather than
userland/PECL are:
- First-class integration with
#[\SensitiveParameter]to ensure safe
handling of sensitive/PII data in stack traces.- 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
Hi Morgan,
That is a very valid and fair point.
The intention was to provide a helpful security default, but you are right:
str_mask()is fundamentally a low-level string manipulation primitive, and masking is also used in non-sensitive contexts (like word games, anonymized identifiers, or formatting). Treating it as a generic string primitive without baking in domain-specific assumptions makes total sense.I will remove the
#[\SensitiveParameter]attribute from the RFC to keep the function purely general-purpose, consistent with otherext/standardstring functions.Thanks for the insightful feedback!
Best regards,
Sepehr
در تاریخ دوشنبه ۲۱ سپتامبر ۲۰۲۶، ۱۳:۴۹ David Maye david.maye@seigisoft.com
نوشت:
Sorry from the last mail I meant Ensure, not injure. Grammatical character
is on 😅. Sorry everyoneEl 21/09/2026 12:15 David Maye david.maye@seigisoft.com escribió:
Hello dear Sepher.
So I have a question:
The main reasons for considering it at the core level rather than
userland/PECL are:
- First-class integration with
#[\SensitiveParameter]to ensure safe
handling of sensitive/PII data in stack tracesWasn't the objective of the RFC to have first class integration with
#[\SensitiveParameter]?Also as you stated in that sentence, you want to injure handling of
sensitive pii data in stack trades. However that's kind of uses very uses
as a very specific use case. But was he RFC first design for that kind of
use case?Because it is kind of hard to understand it, the reason of the RFC, if in
the same mails you sent also you state that you are rectifying the RFC. It
sounds like the RFC first is designed for a very specific kind of use,
which is very niche, and apart from that it is a very dynamically designed
RFC.It just I'm the kind of lost of why is the using in the first place. If
you can do it in user land perfectly. And in the case of using c, you have
extensions that are not part of the main API but solves a very important
problem and issue. Why is the need in the main PHP API? Because it sounds
like you're trying just to push something into the main API just for the
sake of pushing it, and from each Mail the necessity of using it or it's
consideration at least gradually decreasesKind regards,
David
Hi David,
Thanks for bringing this up — it's a completely fair observation.
You and Morgan are spot on. Having #[\SensitiveParameter] in the initial
signature was conflating two separate concerns: a core string primitive
versus the caller's business context. Sensitivity is a property of the data
being passed, not an intrinsic property of a byte-masking utility.
I have updated the RFC accordingly and removed #[\SensitiveParameter]
from the proposed function signature:
https://wiki.php.net/rfc/str_mask
This keeps the RFC laser-focused on what str_mask() truly aims to be: a
clean, low-level string manipulation primitive. The core justification
remains unchanged:
-
Performance & Zero Intermediate Allocations:
In userland, achieving safe masking requires composingsubstr_replace()
andstr_repeat(), forcing intermediate allocations and buffer copying. A
native C implementation operates directly within the buffer with zero
intermediate overhead — which matters significantly on high-throughput
paths like payload anonymization and access logging. -
Predictable, Fail-Closed Semantics:
Existing userland helpers handle boundary conditions inconsistently. By
introducing a native primitive that adheres strictly to PHP 8+ conventions
(such as standardValueErrorsemantics for out-of-bounds offsets,
identical tosubstr()), framework authors and libraries gain a single,
reliable building block rather than maintaining disparate implementations.
Refining proposals based on community pushback is an essential part of this
process, and stripping the attribute sharpens the focus entirely onto
performance and language consistency.
Best regards,
Sepehr
Okay I have another question about the RFC in order to understand it's well.
There is no need to use string repeat function at all. If I want five hashtags, I can just do "#####", and everything is done 👍.
Because I think people would add the str_repeat function if the data to mask has a very Dynamic length. But if we're talking about phone numbers, credit cards, they all have the same predefined length in the entire world (in the case of phone numbers, some uses 10 digits instead of nine. But even this can be fixed pretty easily without additional temporary send string).
So if you do just like that, you are completely avoiding the use of intermediate zend string allocation. Just by replacing an operation with the correct mask, you get exactly the same result we've all needing to create a new global function. And (if I'm not wrong), if you want to implement a function called string mask, you can configure it in such way that it calls substring replace function without an additional Zend string allocation.
And thinking about Cake PHP: if they use the kind of pattern that this RFC that tries to avoid, which is the use of intermediate zend string allocation just for a temporal buffer, then a pull request for that function optimization can be done following that kind of pattern (no string repeat needed)
Because I think there is no need of very rigorous kind of testing just to check if some data will leak. No need to have that kind of safeguards prevents developers from "shooting on their foot" like in the case of developing in a language like C, just for masking that a a string.
So my question is: you should use the correct string mask, with predefined set of characters, inside the substring replace function, then you don't need to string mask function right? Because the performance is nearly the same?
Awesome a 30ms difference for this kind of operation that appears not very often, is it worth it? Considering the use of unnecessary string repeat? What the benchmark shows if you just don't use string replace?
Kind regards,
David Maye. > El 21/09/2026 13:27 سپهر محمودی sepehrphpr@gmail.com escribió:
در تاریخ دوشنبه ۲۱ سپتامبر ۲۰۲۶، ۱۳:۴۹ David Maye <david.maye@seigisoft.com mailto:david.maye@seigisoft.com> نوشت:
Sorry from the last mail I meant Ensure, not injure. Grammatical character is on 😅. Sorry everyone > > > El 21/09/2026 12:15 David Maye <david.maye@seigisoft.com mailto:david.maye@seigisoft.com> escribió:
Hello dear Sepher.
So I have a question:
The main reasons for considering it at the core level rather than userland/PECL are:
- First-class integration with
#[\SensitiveParameter]to ensure safe handling of sensitive/PII data in stack tracesWasn't the objective of the RFC to have first class integration with
#[\SensitiveParameter]?Also as you stated in that sentence, you want to injure handling of sensitive pii data in stack trades. However that's kind of uses very uses as a very specific use case. But was he RFC first design for that kind of use case?
Because it is kind of hard to understand it, the reason of the RFC, if in the same mails you sent also you state that you are rectifying the RFC. It sounds like the RFC first is designed for a very specific kind of use, which is very niche, and apart from that it is a very dynamically designed RFC.
It just I'm the kind of lost of why is the using in the first place. If you can do it in user land perfectly. And in the case of using c, you have extensions that are not part of the main API but solves a very important problem and issue. Why is the need in the main PHP API? Because it sounds like you're trying just to push something into the main API just for the sake of pushing it, and from each Mail the necessity of using it or it's consideration at least gradually decreases
Kind regards,
David > >
Hi David,
Thanks for bringing this up — it's a completely fair observation.
You and Morgan are spot on. Having
#[\SensitiveParameter]in the initial signature was conflating two separate concerns: a core string primitive versus the caller's business context. Sensitivity is a property of the data being passed, not an intrinsic property of a byte-masking utility.I have updated the RFC accordingly and removed
#[\SensitiveParameter]from the proposed function signature:
https://wiki.php.net/rfc/str_maskThis keeps the RFC laser-focused on what
str_mask()truly aims to be: a clean, low-level string manipulation primitive. The core justification remains unchanged:
Performance & Zero Intermediate Allocations:
In userland, achieving safe masking requires composingsubstr_replace()andstr_repeat(), forcing intermediate allocations and buffer copying. A native C implementation operates directly within the buffer with zero intermediate overhead — which matters significantly on high-throughput paths like payload anonymization and access logging.Predictable, Fail-Closed Semantics:
Existing userland helpers handle boundary conditions inconsistently. By introducing a native primitive that adheres strictly to PHP 8+ conventions (such as standardValueErrorsemantics for out-of-bounds offsets, identical tosubstr()), framework authors and libraries gain a single, reliable building block rather than maintaining disparate implementations.Refining proposals based on community pushback is an essential part of this process, and stripping the attribute sharpens the focus entirely onto performance and language consistency.
Best regards,
Sepehr
در تاریخ دوشنبه ۲۱ سپتامبر ۲۰۲۶، ۱۶:۴۰ David Maye david.maye@seigisoft.com
نوشت:
Okay I have another question about the RFC in order to understand it's
well.There is no need to use string repeat function at all. If I want five
hashtags, I can just do "#####", and everything is done 👍.Because I think people would add the str_repeat function if the data to
mask has a very Dynamic length. But if we're talking about phone numbers,
credit cards, they all have the same predefined length in the entire world
(in the case of phone numbers, some uses 10 digits instead of nine. But
even this can be fixed pretty easily without additional temporary send
string).So if you do just like that, you are completely avoiding the use of
intermediate zend string allocation. Just by replacing an operation with
the correct mask, you get exactly the same result we've all needing to
create a new global function. And (if I'm not wrong), if you want to
implement a function called string mask, you can configure it in such way
that it calls substring replace function without an additional Zend string
allocation.And thinking about Cake PHP: if they use the kind of pattern that this RFC
that tries to avoid, which is the use of intermediate zend string
allocation just for a temporal buffer, then a pull request for that
function optimization can be done following that kind of pattern (no string
repeat needed)Because I think there is no need of very rigorous kind of testing just to
check if some data will leak. No need to have that kind of safeguards
prevents developers from "shooting on their foot" like in the case of
developing in a language like C, just for masking that a a string.So my question is: you should use the correct string mask, with predefined
set of characters, inside the substring replace function, then you don't
need to string mask function right? Because the performance is nearly the
same?Awesome a 30ms difference for this kind of operation that appears not very
often, is it worth it? Considering the use of unnecessary string repeat?
What the benchmark shows if you just don't use string replace?Kind regards,
David Maye.
Hi David,
Thank you for bringing this up. It's a great question, and I appreciate the
perspective.
While it is technically possible to achieve masking using a combination of
str_repeat and substr_replace, I believe there are a few reasons why a
dedicated str_mask function is valuable for the PHP core:
-
Performance and Intermediate Allocations: Regarding the performance
difference: I’ve conducted some benchmarks comparing manual user-land
implementations versus the overhead of these operations. You can find the
data here:
https://gist.github.com/sepehrphpr/86c4be78a3f4882dbd58f6598bb92b16.
While a single call might seem negligible, string immutability in PHP means
manual masking forces multiple intermediate zend string allocations and
creates unnecessary pressure on the Garbage Collector. In high-throughput
applications processing millions of strings (e.g., sanitizing logs, PII, or
large database result sets), these hidden overheads accumulate rapidly. A
C-level implementation bypasses this entirely. -
Code Clarity and Maintainability: Real-world data is rarely static
(e.g., variable-length emails, different phone number formats, or varying
credit card lengths). Developers currently have to calculate lengths,
handle offsets, and manage concatenation manually.str_maskencapsulates
this logic into a single, clean, and expressive call, significantly
reducing the surface area for "off-by-one" or logic errors. -
"Pit of Success": PHP’s philosophy often leans towards providing tools
that guide developers toward the "pit of success." Providing a native,
optimized, and safe function ensures that developers are using a
standardized implementation, rather than relying on inconsistent
framework-level helpers or fragile custom code.
I’m aiming for this to be a "low-effort, high-impact" utility that prevents
reinventing the wheel in every project.
Best regards,
Sepehr
در تاریخ شنبه ۱۹ سپتامبر ۲۰۲۶، ۰۲:۱۳ 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
Why is this needed in php itself? Masking seems very use-case specific and
would be better done inside php library with extensible modules for
different types of data to mask.
Ilia Alshanetsky
Technologist, CTO, Entrepreneur
E: ilia@ilia.ws
T: @iliaa
B: http://ilia.ws
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): stringExamples
Masking a credit card (positive offset & length):
$credit_card = '1234567890123456';
$masked_card = str_mask($credit_card, '*', 4, 8);
// Output: 1234********3456Masking a phone number (negative offset to count from the end):
$phone_number = '+989123456789';
$masked_phone = str_mask($phone_number, 'X', -4);
// Output: +9891234XXXXYou can find all the details, implementation plans, and RFC discussions
here:
https://wiki.php.net/rfc/str_maskLooking forward to hearing your feedback and thoughts!
Best regards,
Sepehr Mahmoudi
در تاریخ دوشنبه ۲۱ سپتامبر ۲۰۲۶، ۰۴:۳۱ Ilia ilia@ilia.ws نوشت:
Why is this needed in php itself? Masking seems very use-case specific and
would be better done inside php library with extensible modules for
different types of data to mask.Ilia Alshanetsky
Technologist, CTO, Entrepreneur
E: ilia@ilia.ws
T: @iliaa
B: http://ilia.wsHi 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): stringExamples
Masking a credit card (positive offset & length):
$credit_card = '1234567890123456';
$masked_card = str_mask($credit_card, '*', 4, 8);
// Output: 1234********3456Masking a phone number (negative offset to count from the end):
$phone_number = '+989123456789';
$masked_phone = str_mask($phone_number, 'X', -4);
// Output: +9891234XXXXYou can find all the details, implementation plans, and RFC discussions
here:
https://wiki.php.net/rfc/str_maskLooking forward to hearing your feedback and thoughts!
Best regards,
Sepehr Mahmoudi
Hi Ilia,
Thanks for sharing your perspective.
I completely agree that high-level, schema-aware masking (such as
formatting credit cards, emails, or complex data structures) belongs in
userland libraries and extensible packages.
However, str_mask() is intentionally not designed to be a high-level
formatter or business-logic helper. Instead, it is a low-level, positional
string primitive—analogous to str_pad() or substr_replace()—that simply
replaces a byte range with a repeating character without intermediate
string allocations.
Userland libraries currently implement this through substr_replace($str, str_repeat(...)) or regex loops, incurring repeated string allocations and
edge-case validation overhead. Having a single native primitive provides
the fast, fail-safe foundation that those higher-level libraries can build
upon.
Best regards,
Sepehr
Hi Sepehr
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!
You have been bombarding this list with almost 100 e-mails in the last
two months. This is made worse by well-intentioned people trying to
explain to you what's wrong with your low-effort proposals, but you
continue not to listen. This mailing list is crucial to the project,
it's how we communicate and make changes to the language. It's not a
playground, but you're treating it as such. You need to stop here and
drastically reduce the number of e-mails you send.
To avoid any vagueness: Don't start more than one 1 thread per month,
don't send more than 3 e-mails per week. You can respond to multiple
people in one e-mail if necessary. This is your last warning, if you
fail to comply we will be forced to ban you from the list.
Ilija