Skip to content

[Console] Fixes question input encoding on Windows #37385

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 6, 2020
Merged

[Console] Fixes question input encoding on Windows #37385

merged 1 commit into from
Jul 6, 2020

Conversation

YaFou
Copy link
Contributor

@YaFou YaFou commented Jun 22, 2020

Q A
Branch? 3.4
Bug fix? yes
New feature? no
Deprecations? no
Tickets Fix #35842, Fix #36324, Fix #37495 and Fix #37278
License MIT
Doc PR no

To ask a question to a user, the QuestionHelper use fgets. However, special characters are not supported on Windows with this function (like accents: é, à, ö). The solution is to set a special encoding with sapi_windows_cp_get.

Before

$stream = fopen('php://stdin', 'r');
$input = fgets($stream);
echo $input;

// input: "Bonjour à tous"
// output: 'Bonjour \ tous" or "Bonjour   tous"

After

// Check if the function exists because it only exists on Windows
if(function_exists('sapi_windows_cp_set')) {
    sapi_windows_cp_get(437);
}

$stream = fopen('php://stdin', 'r');
$input = fgets($stream);
echo $input;

// input: "Bonjour à tous"
// output: 'Bonjour à tous"

Thanks to @bnjmnfnk for the solution 😉

@chalasr
Copy link
Member

chalasr commented Jun 24, 2020

Can you please add a test for this? It should run on windows only.

@YaFou
Copy link
Contributor Author

YaFou commented Jun 24, 2020

Can you please add a test for this? It should run on windows only.

Is there a group in PHPUnit to run only on Windows (like @group windows)? Else, how I can do it? I can also check if the function sapi_windows_cp_get exists...

@chalasr
Copy link
Member

chalasr commented Jun 24, 2020

If the function exists only on windows, it should be enough yes.
If you aren't sure, you can use a '\\' === DIRECTORY_SEPARATOR additional check for both code and tests, see e.g.

if ('\\' === \DIRECTORY_SEPARATOR) {
$this->markTestSkipped('This test is not supported on Windows');
}

@YaFou
Copy link
Contributor Author

YaFou commented Jun 24, 2020

Ok thank you! I'm writing it.

@YaFou
Copy link
Contributor Author

YaFou commented Jun 24, 2020

It's difficult to test it because the stream used for QuestionHelper tests is the PHP memory php://memory. However, this issue is only present in the console with php://stdin.
Does anyone have any idea?

@ostrolucky
Copy link
Contributor

ostrolucky commented Jun 26, 2020

@YaFou
Copy link
Contributor Author

YaFou commented Jun 26, 2020

@chalasr
Copy link
Member

chalasr commented Jun 26, 2020

Can you try the following test locally? Without your patch at first, in order to make sure it breaks under your setup.

--TEST--
QuestionHelper should parse special chars on windows input.
--STDIN--
à é
--FILE--
<?php

use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Question\Question;

$vendor = __DIR__;
while (!file_exists($vendor.'/vendor')) {
    $vendor = dirname($vendor);
}
require $vendor.'/vendor/autoload.php';

$output = new ConsoleOutput();
$output->write((new QuestionHelper())->ask(new ArgvInput(), $output, new Question("Special chars\n")));
exit(0);
?>
--EXPECTF--
Special chars
à é

@YaFou
Copy link
Contributor Author

YaFou commented Jun 26, 2020

Can you try the following test locally? Without your patch at first, in order to make sure it breaks under your setup.

--TEST--
QuestionHelper should parse special chars on windows input.
--STDIN--
à é
--FILE--
<?php

use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Question\Question;

$vendor = __DIR__;
while (!file_exists($vendor.'/vendor')) {
    $vendor = dirname($vendor);
}
require $vendor.'/vendor/autoload.php';

$output = new ConsoleOutput();
$output->write((new QuestionHelper())->ask(new ArgvInput(), $output, new Question("Special chars\n")));
exit(0);
?>
--EXPECTF--
Special chars
à é

No more results... I think PHPUnit simulate and standard input but does not take all behaviors of the fgets function.

Copy link
Member

@chalasr chalasr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works for me as is.

@nicolas-grekas
Copy link
Member

CP1252 allows more diacritics and is already the default on my French VM. CP850 would be an alternate possibility.

Can you try and take 1252 if possible, 850 otherwise?

@nicolas-grekas
Copy link
Member

Oh, note that codepage 65001 is UTF-8, it would be even better. Can you give it a try?

@YaFou
Copy link
Contributor Author

YaFou commented Jul 1, 2020

CP1252 allows more diacritics and is already the default on my French VM. CP850 would be an alternate possibility.

Can you try and take 1252 if possible, 850 otherwise?

Oh, note that codepage 65001 is UTF-8, it would be even better. Can you give it a try?

Only 1252 and 850 work (and 437 of course). I think the better code page is 1252 because it provides a better characters support.

@nicolas-grekas
Copy link
Member

Does the function return false is your case with 65001?
If yes, we could set(65001) ?: set(1252) maybe?

@YaFou
Copy link
Contributor Author

YaFou commented Jul 1, 2020

Does the function return false is your case with 65001?
If yes, we could set(65001) ?: set(1252) maybe?

No it returns true but replaces à by nothing or \.

@chalasr
Copy link
Member

chalasr commented Jul 6, 2020

Thanks for fixing this bug @YaFou.

@chalasr chalasr merged commit 4297897 into symfony:3.4 Jul 6, 2020
@YaFou YaFou deleted the console-encoding branch July 31, 2020 18:20
@lpj145
Copy link

lpj145 commented Aug 5, 2020

I'm facing this issue again in 5.2.x-dev, i'm using windows 10, before i ask question, all utf8 chars are wrong.

@YaFou
Copy link
Contributor Author

YaFou commented Aug 6, 2020

I'm facing this issue again in 5.2.x-dev, i'm using windows 10, before i ask question, all utf8 chars are wrong.

Thank you for your feedback @lpj145. Can you provide a screenshot from your terminal? Are you on Windows?

@vaites
Copy link

vaites commented Oct 8, 2020

With this change, the value returned by the helper on Windows is a binary string. Take this example command:

<?php

namespace App\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;

class TestCommand extends Command
{
    protected static $defaultName = 'app:test';

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $helper = $this->getHelper('question');
        $question = new Question('What? ');

        dump($helper->ask($input, $output, $question));

        return Command::SUCCESS;
    }
}

This is the output for the input martínez:

C:\Develop\htdocs\testing\symfony>php bin\console app:test
What? martínez
^ b"martínez"

If I remove the call to sapi_windows_cp_get() the result is this:

C:\Develop\htdocs\testing\symfony>php bin\console app:test
What? martínez
^ "mart\x00nez"

This issue was initially reported here, but after some tests I think the issue is caused by this change. I tested it using three terminal emulators: CMD, PowerPhell and Git Bash:

symfony-ask-encoding

@r3sist
Copy link

r3sist commented Apr 18, 2021

I have a similar issue on Windows 10, Symfony 5.2.6, Hungarian OS environment and chars like áíűőüöúóé.

After first ansfer output gets wrong charset even if I don't use any special characters in questions or answers. And it's not just the output, all the inner encoding settings become strange, e.g. An exception has been thrown during the rendering of a template ("Invalid UTF-8 string.") exception during execution.

Works:

$command = $this->getApplication()->find('generate');

$generateArguments = [
    'project' => 'local',
];

return $command->run(new ArrayInput($generateArguments), $output);

Error:

$helper = $this->getHelper('question');

$projectQuestion = new Question("Projekt: </> \n", 'local');
$project = $helper->ask($input, $output, $projectQuestion);

$command = $this->getApplication()->find('generate');

$generateArguments = [
    'project' => $project,
];

return $command->run(new ArrayInput($generateArguments), $output);

nicolas-grekas added a commit that referenced this pull request May 7, 2021
This PR was submitted for the 5.x branch but it was squashed and merged into the 5.2 branch instead.

Discussion
----------

[Console] Fix Windows code page support

| Q             | A
| ------------- | ---
| Branch?       | 5.2
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | Fix #37385, Fix #35842, Fix #36324, Fix #37495, Fix #37278
| License       | MIT

Corrects previous fixes that dealt with the mojibake problem on Windows where an OEM code page was applied to an input string and then messed with PHP.internal_encoding setting used by the script. This caused strings with different encodings to be displayed on the console output.

Commits
-------

be68682 [Console] Fix Windows code page support
nicolas-grekas added a commit that referenced this pull request May 13, 2021
This PR was merged into the 4.4 branch.

Discussion
----------

[Console] Fix Windows code page support

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | Fix #37385, Fix #35842, Fix #36324, Fix #37495, Fix #37278
| License       | MIT

Corrects mojibake problem on Windows where an OEM code page was applied to an input string and then messed with PHP.internal_encoding setting used by the script. This caused strings with different encodings to be displayed on the console output.

Commits
-------

4145278 [Console] Fix Windows code page support
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants