phpbashcommand-line-interfacezshreadline

php's readline() method adding weird line breaks when executed via shell?


So I have this really weird issue where I have the following dummy.php script:

<?php
declare(strict_types=1);

$prompt = "Hello there!".
    PHP_EOL . "What do you want to know?" .
    PHP_EOL . "Proceed by specifying what you want to know:" .
    PHP_EOL .":  " . PHP_EOL;

readline($prompt);

When I execute the script via php, I get the correct output.

When I place a zsh / bash script in the same directory as the PHP script above, with the contents:

php "./dummy.php";

I get the same output.

When I change the contents of dummy.php to:

<?php
declare(strict_types=1);

$prompt = "Hello there!".
    PHP_EOL . "What do you want to know?" .
    PHP_EOL . "Proceed by specifying what you want to know:" .
    PHP_EOL . "Proceed by specifying what you want to know:" .
    PHP_EOL . "Proceed by specifying what you want to know:" .
    PHP_EOL .":  " . PHP_EOL;

readline($prompt);

I still get the same for both.

When changing it to (added on line of Proceed by specifying what you want to know:):

<?php
declare(strict_types=1);

$prompt = "Hello there!".
    PHP_EOL . "What do you want to know?" .
    PHP_EOL . "Proceed by specifying what you want to know:" .
    PHP_EOL . "Proceed by specifying what you want to know:" .
    PHP_EOL . "Proceed by specifying what you want to know:" .
    PHP_EOL . "Proceed by specifying what you want to know:" .
    PHP_EOL .":  " . PHP_EOL;

readline($prompt);

I get the following when executing it via PHP:

Hello there!
What do you want to know?
Proceed by specifying what you want to know:
Proceed by specifying what you want to know:
Proceed by specifying what you want to know:
Proceed by specifying what you want to know:
: 

And the following when executing with the shell script:

Hello there!
What do you want to know?
Proceed by specifying what you want to know:
Proceed by specifying what you want to know:
Proceed by specifying what you want to know:
ying what you want to know:
:  

What I've tried so far (none of them solved the issue):

If relevant, I am trying this on a mac on the latest software state, where zsh is the default shell, on PHP 8.2.

Why is this happening? It is essential because I am writing multiple interactive CLI automations in PHP using readline, and this issue seems to randomly modify the $prompt provided to readline(), which breaks the whole purpose.

Of course the alternative is to execute all scripts via php in the automations, but doing so via zsh / bash is preferred, as all the other processes of the system are also shell scripts.


Solution

  • Yes, this is one of the old bug that makes readline hard to use for complex CLI programs (not only PHP).

    If you are catching user keys in your program, using readline might breaks the STDIN flow., and has it's own logic for new lines.

    So, either, use only readline for simple user inputs, or use another PHP built-in function to catch the STDIN, such as stream_get_line().

    The following is not exactly a readline replacement, however it won't break the STDIN pipe, won't have the new line issue you shown, so that logic could be used from within really complex CLI programs. In any cases, It's great to play around.

    In this example only one character is expected.

    
    // $user = readline();
    stream_set_blocking(STDIN, true);
    $user = stream_get_line(STDIN, 1, PHP_EOL);
    
    if ($user === "y") {
        doSomething();
    }
    if ($user === "n") {
        break;
    }
    if ($user === "q") {
        exit;
    }