PHP: Is it possible to get non blocking output buffer? - php

I'm building a console application and would like to use already written code without modifying it. But, I need the output of a command. Is it possible to do something like this:
function func() {
/* Let's say this function contains already written
code and it would be easier to rewrite it than modify */
echo 'Confirm action';
$input = fgets(fopen('php://stdin', 'r'));
if ($input == 'y') echo 'okay';
else echo 'no';
return 0;
}
$code = func();
// func() returns non zero only on error and confirming/declining an action returns 0.
// I want to know if the action was confirmed.
// Using ob_start() prevents echo from working in the function,
// i.e. the user sees a blank screen waiting for input.
Is this even possible?
I'm writing this with Yii framework. Any ideas appreciated.

Solved this with popen, e.g.:
$handle = popen('php yiic migrate create '.$name, 'r');
$output = '';
while (!feof($handle))
{
$read = fread($handle, 2096);
$output .= $read;
echo $read;
}
$exitCode = pclose($handle);

Related

How to assign SplFileObject::fpassthru output to variable

I'm currently writing some data to an SplFileObject like this:
$fileObj = new SplFileObject('php://text/plain,', "w+");
foreach($data as $row) {
$fileObj->fputcsv($row);
}
Now, I want to dump the whole output (string) to a variable.
I know that SplFileObject::fgets gets the output line by line (which requires a loop) but I want to get it in one go, ideally something like this:
$fileObj->rewind();
$output = $fileObj->fpassthru();
However, this does not work as it simply prints to standard output.
There's a solution for what I'm trying to achieve using stream_get_contents():
pass fpassthru contents to variable
However, that method requires you to have direct access to the file handle.
SplFileObject hides the file handle in a private property and therefore not accessible.
Is there anything else I can try?
After writing, do a rewind() then you can read everything. The example is for understanding:
$fileObj = new SplFileObject('php://memory', "w+");
$row = [1,2,'test']; //Test Data
$fileObj->fputcsv($row);
$fileObj->rewind();
//now Read
$rowCopy = $fileObj->fgetcsv();
var_dump($row == $rowCopy);//bool(true)
$fileObj->rewind();
$strLine = $fileObj->fgets(); //read as string
$expected = "1,2,test\n";
var_dump($strLine === $expected); //bool(true)
//several lines
$fileObj->rewind();
$fileObj->fputcsv(['test2',3,4]);
$fileObj->fputcsv(['test3',5,6]);
$fileObj->rewind();
for($content = ""; $row = $fileObj->fgets(); $content .= $row);
var_dump($content === "test2,3,4\ntest3,5,6\n"); //bool(true)
If you absolutely have to fetch your content with only one command then you can do this too
// :
$length = $fileObj->ftell();
$fileObj->rewind();
$content = $fileObj->fread($length);
getSize() doesn't work here.
In the absence of an inbuilt function I've decided to do php output buffering as #CBroe had suggested.
...
$fileObj->rewind();
ob_start();
$fileObj->fpassthru();
$buffer = ob_get_clean();
See #jsplit's answer for a better method using SplFileObjects inbuilt functions

How to check if bash input is being redirect to PHP script?

I'm writing a PHP script and I would like to be able to optionally use a file as the script input. This way:
$ php script.php < file.txt
I'm, actually, able to do that using file_get_contents:
$data = file_get_contents('php://stdin');
However, if I don't pass a file to the input, the scripts hangs indefinetelly, waiting for an input.
I tried the following, but it didn't work:
$data = '';
$in = fopen('php://stdin', 'r');
do {
$bytes = fread($in, 4096);
// Maybe the input will be empty here?! But no, it's not :(
if (empty($bytes)) {
break;
}
$data .= $bytes;
} while (!feof($in));
The script waits for fread to return a value, but it never returns. I guess it waits for some input the same way file_get_contents does.
Another attempt was made by replacing the do { ... } while loop by a while { ... }, checking for the EOF before than trying to read the input. But that also didn't work.
Any ideas on how can I achieve that?
You can set STDIN to be non-blocking via the stream_set_blocking() function.
function stdin()
{
$stdin = '';
$fh = fopen('php://stdin', 'r');
stream_set_blocking($fh, false);
while (($line = fgets($fh)) !== false) {
$stdin .= $line;
}
return $stdin;
}
$stdin = stdin(); // returns the contents of STDIN or empty string if nothing is ready
Obviously, you can change the use of line-at-a-time fgets() to hunk-at-a-time fread() as per your needs.

Perform a mathematical operation after retrieving from another file

I have a text file (math.txt) in which any kind of arithmetic operation could be written. I have to read the file using PHP and determine the output. I am using the below mentioned code to read the content of the file.
$file = 'math.txt'; // 2+3 is written in math.txt
$open = fopen($file, 'r');
$read = fgets($open);
$close = fclose($open);
Using the above code, i am getting the content. But echoing the content is displaying the original content (i.e 2+3) rather than displaying the output(i.e 5). I am not understanding what should i do in this case.
Any help on this will be appreciated. Thanks in advance.
But echoing the content is displaying the original content (i.e 2+3)
rather than displaying the output(i.e 5).
This is completely expected behaviour. You read a string from a file. How should PHP know that you want it to calculate the expression?
You have to implement a simple parser (or search one on the Internet) which analyses the expression and caulates the result.
dave1010 provided a very nice function in one of his posts:
function do_maths($expression) {
eval('$o = ' . preg_replace('/[^0-9\+\-\*\/\(\)\.]/', '', $expression) . ';');
return $o;
}
echo do_maths('1+1');
But note that this can still halt your script execution if the input contains a syntax error!
Here is a better library which uses a real parser: https://github.com/stuartwakefield/php-math-parser
read the file parse according to operator
like file=2*5;
$open = fopen($file, 'r');
$read = fgets($open);
$key = preg_split("/[*+-\/]+/", $read);
$operator= substr($a, strpos($a,$key[1])-1,1);
if($operator=='+')
{
echo $key[0]+ $key[1];
}
else if($operator=='-')
{
echo $key[0]- $key[1];
}
else if($operator=='*')
{
echo $key[0]* $key[1];
}
else if($operator=='/')
{
echo $key[0]/$key[1];
}

How to read multiple lines from socket stream?

I want to retrieve email from gmails' imap server but the problem is that the responses from the server are multiple lines long (as demonstrated here) and fgets only retrieves one line.
I've tried using fgets, fread, socket_read but none of them work so either i'm using the wrong method or using the methods incorrectly. I also tried this tutorial but it didn't work either. I would appreciate if someone could help me with this.
Thanks and i'm really sorry if this is an amateur question.
Code:
<?php
$stuff = fsockopen('ssl://imap.gmail.com',993);
$reply = fgets($stuff,4096);
echo 'connection: '.$reply.'<br/>';
$request = fputs($stuff,"a1 LOGIN MyUserName Password\r\n");
$receive = socket_read($stuff, 4096);
echo 'login: '.$receive.'<br/>';
$request = fputs($stuff,"a2 EXAMINE INBOX\r\n");
$reply = '';
while(!feof($stuff))
$reply .= fread($stuff, 4096);
echo $reply;
/*
$request = fputs($stuff,'a3 FETCH 1 BODY[]\r\n');
$reply = fgets($stuff);
echo $reply;
*/
?>
Max's answer below works. This is my implementation of it.
private function Response($instructionNumber)
{
$end_of_response = false;
while (!$end_of_response)
{
$line = fgets($this->connection,self::responseSize);
$response .= $line.'<br/>';
if(preg_match("/$instructionNumber (OK|NO|BAD)/", $response,$responseCode))
$end_of_response = true;
}
return array('code' => $responseCode[1],
'response'=>$response);
}
Generally, you know to stop reading when you get the OK/BAD/NO response for the tag you sent. If you send a1 LOGIN ... you stop when you get a1 OK/BAD/NO ....
It's been a while since I wrote PHP, and I don't know that much about IMAP, but if it's anything like NNTP, your code would look a bit like this (wrote it in the SO editor, might be bugged) :
$buffer = '';
function read_line($socket) {
global $buffer;
while (strpos($buffer, "\n") === false)
$buffer .= fread($socket, 1024);
$lineEnd = strpos($buffer, "\n");
$line = substr($buffer, 0, $lineEnd-1);
$buffer = substr($buffer, $lineEnd);
return $line;
}
function send_line($socket, $line) {
fwrite($socket, $line);
}
$socket = fsockopen('ssl://imap.gmail.com',993);
$welcome = read_line($socket);
send_line("a1 LOGIN MyUserName Password\r\n");
$reply = read_line($socket);
send_line("a2 EXAMINE INBOX\r\n");
while (($reply = trim(read_line($socket))) != '.') {
echo $reply.PHP_EOL;
}
echo "Done";
The basic concepts are :
Always buffer all incoming data. PHP doesn't handle lines very well, so do the splitting yourself.
Don't randomly read everything, but know what to expect. You expect one welcome line, LOGIN has one reply, and EXAMINE INBOX keeps outputting data until there's a single dot, so immediately stop reading once you see that.
You'll most likely want a simple function to take care of the reading. You could even write another function to make it easy:
function read_block($socket) {
$block = '';
while ('.' != trim($reply = read_line($socket)) {
$block .= $reply;
}
return $block;
}

How do I write a command-line interactive PHP script?

I want to write a PHP script that I can use from the command line. I want it to prompt and accept input for a few items, and then spit out some results. I want to do this in PHP, because all my classes and libraries are in PHP, and I just want to make a simple command line interface to a few things.
The prompting and accepting repeated command line inputs is the part that's tripping me up. How do I do this?
The I/O Streams page from the PHP manual describes how you can use STDIN to read a line from the command line:
<?php
$line = trim(fgets(STDIN)); // reads one line from STDIN
fscanf(STDIN, "%d\n", $number); // reads number from STDIN
?>
From PHP: Read from Keyboard – Get User Input from Keyboard Console by Typing:
You need a special file: php://stdin which stands for the standard input.
print "Type your message. Type '.' on a line by itself when you're done.\n";
$fp = fopen('php://stdin', 'r');
$last_line = false;
$message = '';
while (!$last_line) {
$next_line = fgets($fp, 1024); // read the special file to get the user input from keyboard
if (".\n" == $next_line) {
$last_line = true;
} else {
$message .= $next_line;
}
}
I'm not sure how complex your input might be, but readline is an excellent way to handle it for interactive CLI programs.
You get the same creature comforts out of it that you would expect from your shell, such as command history.
Using it is as simple as:
$command = readline("Enter Command: ");
/* Then add the input to the command history */
readline_add_history($command);
If available, it really does make it simple.
Here a typical do-case-while for console implementation:
do {
$cmd = trim(strtolower( readline("\n> Command: ") ));
readline_add_history($cmd);
switch ($cmd) {
case 'hello': print "\n -- HELLO!\n"; break;
case 'bye': break;
default: print "\n -- You say '$cmd'... say 'bye' or 'hello'.\n";
}
} while ($cmd!='bye');
where user can use arrows (up and down) to access the history.
I found an example on PHP.net, Utiliser PHP en ligne de commande:
$handle = fopen("php://stdin", "r");
$line = fgets($handle);
if (trim($line) != 'yes') {
...
Simple:
#!/usr/bin/php
<?php
define('CONFIRMED_NO', 1);
while (1) {
fputs(STDOUT, "\n"."***WARNING***: This action causes permanent data deletion.\nAre you sure you're not going to wine about it later? [y,n]: ");
$response = strtolower(trim(fgets(STDIN)));
if( $response == 'y' ) {
break;
} elseif( $response == 'n' ) {
echo "\n",'So I guess you changed your mind eh?', "\n";
exit (CONFIRMED_NO);
} else {
echo "\n", "Dude, that's not an option you idiot. Let's try this again.", "\n";
continue;
}
}
echo "\n","You're very brave. Let's continue with what we wanted to do.", "\n\n";
My five cents:
Using STDOUT and STDIN:
fwrite(STDOUT, "Please enter your Message (enter 'quit' to leave):\n");
do{
do{
$message = trim(fgets(STDIN));
} while($message == '');
if(strcasecmp($message, 'quit') != 0){
fwrite(STDOUT, "Your message: ".$message."\n");
}
}while(strcasecmp($message,'quit') != 0);
// Exit correctly
exit(0);
The algorithm is simple:
until done:
display prompt
line := read a command line of input
handle line
It's very trivial to use an array that maps commands to callback functions that handle them. The entire challenge is roughly a while loop, and two function calls. PHP also has a readline interface for more advanced shell applications.
One line of code (line 2):
<?php
$name = trim(shell_exec("read -p 'Enter your name: ' name\necho \$name"));
echo "Hello $name, this is PHP speaking\n";
exit;
Checkout this answer's source in blog post How can I capture user input from the cmd line using PHP?.
Basically you read from standard input. See Input/output streams.

Categories