I'm creating a library and I want to test a system that asks the user by input in the console. I use PHPUnit as a testing library but and I don't know how to execute readline function and then print a text to answer the input to test it.
Here it is my test:
$app = new Application;
$i = null;
$app->registerCommand('test', function(Input $input) use(&$i){
// $input->getInput() asks the user and returns the value
$i = $input->getInput();
});
$this->expectOutputString("\n");
// Run the application, so asks the user to enter a value
$app->run();
// Here, I want force to enter a value by the code
$this->assertSame($i, 'test');
I've tested to replace readline by fgets(STDIN) but without results. I thought to solve the problem with asynchronous callables but I need guide for good libraries to use.
Here it is differents ways I've tested in getInput function:
return readline();
return trim(fgets(STDIN));
Thank for your help.
Try replacing the use of read line or some 3rd party prompt command with your own function and use the following:
You can read keyboard input manually in php with this:
$fp = fopen("php://stdin", "r");
$input = rtrim(fgets($fp, 1024));
Note the string with std input used as the input stream.
And then when testing replace the input stream with a file like so
$fp = fopen(__DIR__ . '/../test/test_input', "r");
$input = rtrim(fgets($fp, 1024));
This time it will read a single line from the file instead.
Obviously you will have to make a function and some way to swap the input stream string during testing but the above is all you need to get it working.
Here is a working unit test:
class PromptTest extends TestCase
{
public function testPrompt()
{
$fp = fopen(__DIR__ . '/../test/test_input', "r");
$rtrim = rtrim(fgets($fp, 1024));
self::assertEquals('first line of file', $rtrim);
}
}
This produced
OK (1 test, 1 assertion)
ALso found here: Is there a way to access a string as a filehandle in php?
that a string input can be used.
Related
I would like to create a < input> where if someone enters text, a text file will add the content entered as a new row. I have tried highly modified the feature in this link: here
Just use the PHP_EOL (PHP end of line) constant that will create a new line.
This must be appended at the end of each line.
$file = fopen("myfile.txt", "a+");
fwrite($file, "hello".PHP_EOL);
// or...
fwrite($file, $myvar.PHP_EOL);
Alternatively, you could create your own, new, function:
function fwrite2($handle, string $string, $length = null, $newline = true) {
$string = $newline ? $string.PHP_EOL : $string;
if (isset($length)) {
fwrite($handle, $string, $length);
} else {
fwrite($handle, $string);
}
}
Call the above in the same manner, except the third argument will now create a new line automatically.
Edit following the comments:
The a+ means that the file is open and stored in $file and is available for reading and writing. The a stands for append; meaning the fwrite will append the file.
See more on the PHP documentation.
$file = fopen("myfile.txt", "a+");
fwrite2($file, "{$_GET['message']} | from {$_GET['sender']}");
Since you are using the URL to send data (ill-advised, but that is another point completely), you can access its contents through the superglobal variable - $_GET.
Notice that I have wrapped the values in curly braces. This is because $_GET is an array and if you want to interpolate arrays they must be wrapped, the same goes for class properties.
I am making a script that accepts data input both via:
filename (it opens a .txt file for reading)
pipe (reading via "php://stdin")
In both cases, I need to get controls from keyboard, stdin;
1. When opening the file not using pipe(stdin) I can read the keyboard from
./myscript.php --file filename.txt
then
<?php
$DATA = file_get_contents("filename.txt");
// etc loop
$input = fopen("php://stdin", "r"); // works
stream_set_blocking($input, false);
$key = fgetc($input); // get input from keyboard
echo $key;
?>
2. But when using pipe, this does not work, example:
cat filename.txt | ./myscript.php
then
<?php
$DATA = file_get_contents("php://stdin");
// etc loop
$input = fopen("php://stdin", "r"); // does not works
stream_set_blocking($input, false);
$key = fgetc($input); // get input from keyboard
echo $key;
?>
Why can't I read from keyboard in the second case? Thanks.
I was able to do it reading directly from /dev/tty
<?php
$DATA = file_get_contents("php://stdin");
// etc loop
$input = fopen("/dev/tty", "r"); // this works, as stdin == pipe
stream_set_blocking($input, false);
$key = fgetc($input); // get input from keyboard
echo $key;
?>
I found the answer here:
How to make Perl use different handles for pipe input and keyboard input?
Also:
Can I prompt for user input after reading piped input on STDIN in Perl?
I have a php script that is called and it executes a shell command through shell_exec(). This shell command requires multiple user inputs at different stages. I am stuck with getting the interactivity piece working at each input stage.
This is just an example of how I imagine it working...
<?php
$return = shell_exec('runcmd');
//Let runcmd run until first break
echo $return;
$userinput1 = 'foo';
//Let runcmd run until next break
echo $return;
$userinput2 = 'bar';
//Let runcmd run until nth break
echo $return;
$userinputNth = 'nth';
To feed input to a shell command, use popen.
$handle = popen('runcmd', 'w');
fputs($handle, 'foo
bar
nth
');
pclose($handle);
Since output is not captured, we needn't echo it.
I've got a php file echoing hashes from a MySQL database. This is necessary for a remote program I'm using, but at the same time I need my other php script opening and checking it for specified strings POST parsing. If it checks for the string pre-parsing, it'll just get the MySQL query rather than the strings to look for.
I'm not sure if any functions do this. Does fopen() read the file prior to parsing? or file_get_contents()?
If so, is there a function that'll read the file after the php and mysql code runs?
The file with the hashes query and echo is in the same directory as the php file reading it, if that makes a difference.
Perhaps fopen reads it post-parse, and I've done something wrong, but at first I was storing the hashes directly in the file, and it was working fine. After I changed it to echo the contents of the MySQL table, it bugged out.
The MySQL Query script:
$query="SELECT * FROM list";
$result=mysql_query($query);
while($row=mysql_fetch_array($result, MYSQL_ASSOC)){
echo $row['hash']."<br>";
}
What I was using to get the hash from this script before, when it was just a list of hashes:
$myFile = "hashes.php";
$fh = fopen($myFile, 'r');
$theData = fread($fh, filesize($myFile));
fclose($fh);
$mystring = $theData;
$findme = $hash;
$pos = strpos($mystring, $findme);
The easiest thing to do would be to modify your first php file which echoes everything, along these lines:
change every instance of echo to e.g. $data[] =
at the bottom, do foreach($data as $d) echo $d (this will produce the same result as you have right now)
you now still have your $data array which you can loop through and do whatever you like with it.
To provide working code examples, it would be great if you could post the current code of your file.
EDIT
If you change your script like so:
$query="SELECT * FROM list";
$result=mysql_query($query);
while($row=mysql_fetch_array($result, MYSQL_ASSOC)){
$data[] = $row['hash']."<br />";
}
foreach($data as $d) {
echo $d;
}
...you'll have the array $data that contains each hash in a key. You can then loop through this array like so:
foreach($data as $d) {
//do something
}
I'm writing a PHP app that has a 'control panel' that writes a prefs file with certain variables. On every POST, if the file doesn't exist, it is created. If it does exist, it is unlinked and a new file is touched with the same filename and new variables. This file is then included on another page with displays content based on the variables inside it.
$file = "phpsettings.php";
if (!file_exists($file)) {
touch($file);
$handle = fopen ($file, 'r+');
$str = "<?php \$pref1 = \"$mypref\"; ?>";
} else {
unlink($file);
touch($file);
$handle = fopen ($file, 'r+');
$str = "<?php \$pref1 = \"$mypref\"; ?>";
}
fwrite ($handle, $str);
fclose ($handle);
Is this a safe way of writing preferences, provided this file will be overwritten many times per day? What is a good way of both alerting the user of this control panel if the file wasn't saved correctly, and in that case, what would be a good contingency plan to avoid breaking the page this prefs file is included on short of defining a default set of variables to fill if !(file_exists)?
If you store your settings in an array, you can serialize() them and write to a text file, rather than writing raw php to a php file and including it.
If you're not sanitising your input for those preferences, and say $mypref1 represents someone's name, there's nothing stopping them from filling this out in the form field:
\"; echo \"PWNED
and your resulting PHP will become
<?php \$pref1 = \"$mypref\"; echo \"PWNED\"; ?>
So firstly, storing your preferences in an array and using serialize() is much safer:
$prefs = array('mypref1' => 'somethingorother');
$handle = fopen ($file, 'w');
fwrite($handle, serialize($prefs));
fclose($h);
// example code demonstrating unserialization
$prefs2 = unserialize(file_get_contents($file));
var_dump($prefs == $prefs2); // should output "(bool) true"
In your question, you also mention that if the file does exist, it is unlinked. You can simply truncate it to zero length by passing "w" as the second argument to fopen - you don't need to manually delete it. This should set the mtime anyway, negating the need for the call to touch().
If the values being written to the file are preferences, surely each preference could have a default, unless there are hundreds? array_merge will allow you to overwrite on a per-key basis, so if you do something like this:
// array of defaults
$prefs = array(
'mypref1' => 'pants',
'mypref2' => 'socks',
);
if (file_exists($file)) {
// if this fails, an E_NOTICE is raised. are you checking your server error
// logs regularly?
if ($userprefs = unserialize(file_get_contents($file))) {
$prefs = array_merge($prefs, $userprefs);
}
}
If the issue is that there are heaps, and you don't want to have to initialise them all, you could have a get_preference method which just wraps an isset call to the prefs array.
function get_preference($name, &$prefs) {
if (isset($pref[$name]))
return $pref[$name];
return null;
}
var_dump(get_preference('mypref1', $prefs));
Beyond all of the questions this raises though, the reality is that with your app, in the unlikely event that something does go wrong with the fopen, it should be regarded as a serious failure anyway, and the handful of users you're likely to have making use of this feature are going to be contacting you pretty darn quick if something goes wrong.
It is always better to store your users state in a session and only persist that state when needed.
Why not just use the truncation capabilities of fopen()? I believe instead of "r+", you'll need to pass "w+"... Then if the file exists, it will be truncated, if it doesn't you'll just create a new file. So the code becomes:
$file = "phpsettings.php";
$handle = fopen( $file, 'w+' );
$str = "<?php \$pref1 = \"$mypref\"; ?>";
fwrite ($handle, $str);
fclose ($handle);