Skip to content

str_word_count behaves differently on macos 14 vs 15+ #19828

Closed

Description

@DanielRuf

Description

The following code:

<?php

echo str_word_count('мама', 0, null);

Resulted in this output:

4

But I expected this output instead:

0

It seems the issue started appearing in macos 15+:
https://github.com/DanielRuf/msphpsql-issue/actions/runs/17699243588/job/50302685774 (macos 14 is green)
https://github.com/DanielRuf/msphpsql-issue/actions/runs/17699284860/job/50302784288 (macos 15 is red)

See also the discussion at microsoft/msphpsql#1532

I could only test via GitHub Actions and https://github.com/shivammathur/setup-php since I do not own a Macbook anymore. So this needs further testing to verify, if this is really an issue with the original PHP releases or only with custom built releases using tools like homebrew (which compiles PHP from source if no matching binary is available).

PHP Version

PHP 8.4.12 (cli) (built: Aug 26 2025 13:36:28) (NTS)
Copyright (c) The PHP Group
Built by Shivam Mathur
Zend Engine v4.4.12, Copyright (c) Zend Technologies

Operating System

macos

Activity

  1. Girgias commented on Sep 13, 2025

    @Girgias
    Member

    What happens if you call setlocale() explicitly prior to the call?

    As this seems like MacOS changed how locales behave, which is not a PHP issue.

  2. DanielRuf commented on Sep 13, 2025

    @DanielRuf
    ContributorAuthor

    Without any arguments?

    Not sure, I'm just providing the details from the original reporters of the issue who thought it was a regression caused by the MS SQL driver / SQLSRV extensions.

    I can do a bit more testing via GitHub Actions using https://github.com/DanielRuf/msphpsql-issue, but need clear instructions.

    Without mbstring:
    https://github.com/DanielRuf/msphpsql-issue/actions/runs/17699512200/job/50303333860#step:5:9 (ubuntu, result: 0)
    https://github.com/DanielRuf/msphpsql-issue/actions/runs/17699512200/job/50303333858#step:5:9 (mac os 14, result: 0)
    https://github.com/DanielRuf/msphpsql-issue/actions/runs/17699512200/job/50303333856#step:5:9 (macos 15, result: 4)

    LC_ALL values:
    https://github.com/DanielRuf/msphpsql-issue/actions/runs/17699610981/job/50303560883#step:3:5 (ubuntu: not set)
    https://github.com/DanielRuf/msphpsql-issue/actions/runs/17699610981/job/50303560876#step:3:5 (macos 14: en_US.UTF-8)
    https://github.com/DanielRuf/msphpsql-issue/actions/runs/17699610981/job/50303560877#step:3:5 (macos 15: en_US.UTF-8)

    setlocale(LC_ALL, 0):
    LC_CTYPE=C.UTF-8;LC_NUMERIC=C;LC_TIME=C;LC_COLLATE=C;LC_MONETARY=C;LC_MESSAGES=C;LC_PAPER=C;LC_NAME=C;LC_ADDRESS=C;LC_TELEPHONE=C;LC_MEASUREMENT=C;LC_IDENTIFICATION=C (ubuntu)

    C (macos 14)

    C/C.UTF-8/C/C/C/C (macos 15)

    So as you assumed, the locale values seem to have changed from macos 14 to 15, which leads to the changed behavior. Where and how should this be adressed? Doesn't this affect every PHP application by default? And if so, is the PHP core the right place to cover this, the default ini configuration, or the documentation to inform users about this?

  3. DanielRuf commented on Sep 13, 2025

    @DanielRuf
    ContributorAuthor

    Because in beyondcode/herd-community#1282 other people pointed out:

    After updating PHP to its latest versions (8.4.4 and 8.3.17), the default value of the locale variables changed. PHP started using the locale variables from the OSX operating system. In my case, this becomes a problem because the software I work with does not have the same locale settings as mine.

    But as we can see there, macos 15 is used. So not exactly sure if they just oversaw that a newer macos version was installed and introduced this.

    What would be the correct approach in this situation?
    Is further bisecting needed from my side or is the amount of provided information sufficient?

  4. iluuu1994 commented on Sep 16, 2025

    @iluuu1994
    Member

    HI @DanielRuf. From the documentation:

    https://www.php.net/manual/en/function.str-word-count.php

    Note that multibyte locales are not supported.

    You should use mbstring for multibyte strings. str_word_count() only works properly for ASCII strings. After all, wouldn't 0 also be wrong?

  5. DanielRuf commented on Sep 16, 2025

    @DanielRuf
    ContributorAuthor

    You should use mbstring for multibyte strings.

    This absolutely makes sense, what you and the documentation say.
    Currently I'm just trying to place the reported behavior change in the correct issue tracker, since it seems others were falsely pointing to other potential causes.

    After all, wouldn't 0 also be wrong?

    Not sure, maybe @mpociot and @gr-leo-carmo as original reporters of the changed behavior can chime in.
    Since my tests on Linux and Windows yield 0, I guess this is the expected return value?

  6. iluuu1994 commented on Sep 16, 2025

    @iluuu1994
    Member

    Since my tests on Linux and Windows yield 0, I guess this is the expected return value?

    It's more that multibyte strings are entirely unsupported and the result is unreliable. str_word_count() will loop over individual bytes and pass them to isalpha(), which is not meaningful for multibyte codepoints. Depending on how your operating system interprets bytes above the standard ASCII range, you might get varying results.

  7. DanielRuf commented on Sep 16, 2025

    @DanielRuf
    ContributorAuthor

    It's more that multibyte strings are entirely unsupported and the result is unreliable.

    Sure, but I doubt that this would change much regarding the reproducible change in macos 15.
    If you have a better test case and issue title, please let me know and I can make some updates to the issue.

    Thanks for your help.

    setlocale(LC_ALL, 'C'); seems to restore the previous behavior on macos 15:
    https://github.com/DanielRuf/msphpsql-issue/actions/runs/17775132910/job/50520824508#step:8:9

  8. github-actions commented on Oct 1, 2025

    @github-actions

    No feedback was provided. The issue is being suspended because we assume that you are no longer experiencing the problem. If this is not the case and you are able to provide the information that was requested earlier, please do so. Thank you.

  9. DanielRuf commented on Oct 1, 2025

    @DanielRuf
    ContributorAuthor

    Thanks bot, but there was my feedback.
    I guess this needs some clarification from more PHP maintainers, since we are now in an unclear situation (since this change affects many users and environments by default).

  10. iluuu1994 commented on Oct 1, 2025

    @iluuu1994
    Member

    @DanielRuf I was awaiting feedback from @mpociot and @gr-leo-carmo. Regardless, as mentioned there's really nothing we can do. This change in behavior is due to system isalpha(), which we can't influence, and OP is effectively relying on undefined behavior by passing multibyte strings to str_word_count(). I don't really have more to add, other than use something that actually supports multibytes. E.g. https://3v4l.org/95BuEK.

  11. DanielRuf commented on Oct 1, 2025

    @DanielRuf
    ContributorAuthor

    @iluuu1994 thanks for the additional information, and I agree with you that other functions like in the linked example suit the requirement much better. And thanks for taking the time to explain how it should be handled.

    I think we can keep the issue closed, upstream repos can reference this or the other linked issues.
    Because in the end the change comes from macos itself and should be properly handled via code changes in affected applications.

    Other issues related to this (not here) can be probably closed as "wontfix" then.

    Personally I had the impression that there could be something done in the PHP core, but I guess when Apple decides to change something, we can't do much besides handling it in the userland. And also since it affects many PHP versions, fixing it directly in the application code is the only realistic solution.

    (this reminds me of old cases where browsers introduced breaking changes which as a result negatively affected frontend frameworks and thus broke specific features on many websites - and we always pointed back to the browser vendors to fix that, since we had no control over this - and often nothing happened for a long time so we did the same: we proposed code changes that individual projects could apply as "temporary" workarounds)

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions