I have a very strange problem that I have been unable to find an answer to. I have a PHP function that reads CSV data into an array then returns true if the data was successfully read and passes the array back by reference variable
function ReadCsvDataIntoArray($path, &$headers, &$array, $idFilter = NULL){
if(file_exists($path)){
$fh = fopen($path, 'r');
if($fh){
$headers = fgetcsv($fh);
$rowIdx = 0;
while($row = fgetcsv($fh)){
$addRow = true;
if($idFilter != NULL){
if(isset($row[0])){
if(!in_array($row[0], $idFilter)){
$addRow = false;
}
}
}
if($addRow){
$colIdx = 0;
foreach($row as $val){
$array[$rowIdx][$headers[$colIdx]] = $val;
$colIdx++;
}
$rowIdx++;
}
}
fclose($fh);
return true;
} else {
echo "Unable to open file: ".$path;
}
} else {
echo "CSV doesn't exist: ".$path;
}
return false;
}
If the function returns as true I then check to make sure the array wasn't passed back as null or empty, then sort the data.
if($this->ReadCsvDataIntoArray($client_library_path, $headers, $CSVdata, $log)){
if($CSVData != NULL){
usort($CSVdata, create_function('$a, $b', 'return $a["engagement"] < $b["engagement"];'));
// Do stuff with the sorted array
} else {
echo "CSV data is NULL.\n";
}
I keep getting "CSV data is NULL" from this. If I change the logic to if($CSVData == NULL) or even if(empty($CSVData)) it enters the if statement, attempts to sort the array (which is full, even though the if statement says it's empty) and does stuff with the data.
This is where my second issue comes in. This usort works on my localhost:
usort($CSVdata, function($a, $b) { return $a["scheduled"] < $b["scheduled"]; });
but it doesn't work on the server because of its php version, so I have changed it to:
usort($CSVData, create_function('$a, $b', 'return $a["scheduled"] < $b["scheduled"];'));
But with the create_function version of the usort I get this error message
Warning: usort(): The argument should be an array
I am guessing this has something to do with the fact that my full array is somehow being evaluated as empty and null even when it isn't.
You say this:
…and passes the array back by reference variable…
And this:
If the function returns as true I then check to make sure the array
wasn't passed back as null or empty, then sort the data.
Why are you doing this? If you are checking true or false then checking if it is null or empty what is the value of that? Just check if it is null or empty by doing this instead:
// return true;
return $array;
} else {
echo "Unable to open file: ".$path;
}
} else {
echo "CSV doesn't exist: ".$path;
}
// return false;
return $array;
And then get rid of the return by reference for $array in the interface:
function ReadCsvDataIntoArray($path, &$headers, $array, $idFilter = NULL){
Your true or false logic is probably broken & not worth dealing with correctly at all. But why spend time reinventing the wheel if the value returns null or empty and that is what you are acting on.
Also you can then adjust this $CSVData logic to fit the new structure:
$CSVData = $this->ReadCsvDataIntoArray($client_library_path, $headers, $CSVdata, $log);
if(!empty($CSVData)){
usort($CSVdata, create_function('$a, $b', 'return $a["engagement"] < $b["engagement"];'));
// Do stuff with the sorted array
} else {
echo "CSV data is empty.\n";
}
Also, your whole return true logic is based strictly on the file itself can be opened:
$fh = fopen($path, 'r');
if($fh){
// Code removed for structural illustration purposes.
// ...
// ...
fclose($fh);
return true;
} else {
But you say this; empahsis mine:
I have a PHP function that reads CSV data into an array then returns
true if the data was successfully read…
No. Your logic does not check if the data was read successfully. Your logic simply returns true if the file itself can be read. Which does not mean the contents of the file itself is valid. Have you checked the file itself? Or did you check if this line:
while($row = fgetcsv($fh)){
Actually has values in $row by doing something like this?
echo '<pre>';
print_r($row);
echo '</pre>';
I think that maybe your CSV has line formatting issues. Like it was saved on a Windows machine but is now being read on a Mac OS X or Linux machine or the other way around. Look at this in the documentation for fgetcsv:
Note: If PHP is not properly recognizing the line endings when reading
files either on or created by a Macintosh computer, enabling the
auto_detect_line_endings run-time configuration option may help
resolve the problem.
So perhaps add this line enabling auto_detect_line_endings to your function like this:
function ReadCsvDataIntoArray($path, &$headers, &$array, $idFilter = NULL){
ini_set("auto_detect_line_endings", true);
Related
This is my code:
<?php
$pass = $_GET["pass"];
$user = $_GET["user"];
$next = false;
$file = fopen("info.txt", "r") or die("Something went wrong.");
// Output one line until end-of-file
while(!feof($file)) {
if ($next == true and $pass === fgets($file)){
echo "true";
$next = false;
} else {
echo "false" . "<br>";
$next = false;
}
if (fgets($file) == $user) {
$next = true;
}
}
fclose($file);
?>
and this is info.txt
ch1ck3n
kodero1029
note that this is just a made up password
for example, we go to my website containing this code, https://ch1ck3n.com/login/auth/auth.php?user=ch1ck3n&pass=kodero1029
and it prints false TWICE.
I am making a login system using php and a simple txt document.
the php code reads the txt file lines one by one, and if a line matches the username, that means that the password is on the next line. but if you see, go to the website and it will print false twice.
the $next variable is to indicate that the next line is the password.
What's wrong?
Your issue is because:
if (fgets($file) == $user) {
$next = true;
}
Why is this wrong?
This is the only way that $next is true, but you have $user = $_GET["user"] //ch1ck3n but if you View the Manual it states:
fgets:
Returns a string of up to length - 1 bytes read from the file pointed to by handle. If there is no more data to read in the file pointer, then FALSE is returned.
And see also:
Reading ends when length - 1 bytes have been read, or a newline (which is included in the return value)
(my emphesis)
So you're return value from fgets is a string which includes the line ending, and you're comparing this to a text string ch1ck3n without a line ending. Therefore $next will never be true, so for each line of the file, the if/else statement will return the false option.
Solution:
Strip line endings from your strings:
if (trim(fgets($file)) === $user) {
$next = true;
}
The same will need to be done for all occassions you use fgets.
Security Note:
As mentioned in comments, your method of approach for this issue re: security is absolutely the incorrect way of doing things. You should be using POST requsts or Database references or SESSION cookies to safely associate credentials data with the script.
Don't send sensitive information in URL as this is insecure with or without https: Link
Comment by Felippe Duarte
You are reading two lines per iteration of the loop. Since you only have two lines (maybe three?) that means in a single iteration you are done. Do this:
<?php
$pass = $_GET["pass"];
$user = $_GET["user"];
$next = false;
$file = fopen("info.txt", "r") or die("Something went wrong.");
// Output one line until end-of-file
while(!feof($file)) {
$line = rtrim(fgets($file)); //Newline is included in the fgets result
if ($next == true && $pass === $line){
echo "true";
$next = false;
} else {
echo "false" . "<br>";
$next = false;
}
if ($line === $user) {
$next = true;
}
}
fclose($file);
The reason that you get false twice is (probably) because you may have an empty line at the end of the file.
This question already has answers here:
How to use return inside a recursive function in PHP
(4 answers)
Closed 9 months ago.
I have the following codes and they are not working:
index.php:
include("loadData.php");
$my_var = loadData("myTxt.txt");
var_dump($my_var);
loadData.php:
function loadData($my_file){
if(file_exists($my_file)){
$file_contents = file_get_contents($my_file);
$file_contents = json_decode($file_contents);
}else{
// If file doesn't exist, creates the file and runs the function again
$data_to_insert_into_file = simplexml_load_file("http://site_with_content.com");
$fp = fopen($my_file, "w");
fwrite($fp, json_encode($data_to_insert_into_file));
fclose($fp);
// Since the file is created I will call the function again
loadData($my_file);
return;
}
// Do things with the decoded file contents (this is suposed to run after the file is loaded)
$result = array();
$result = $file_contents['something'];
return $result;
}
This works as expected in the second time (after the file is created), I can display info on index.php, but in the first time I run (before the file is created) it always displays $result as NULL, I can't understand why since I call the function again...
Any idea?
Thank you
You don't return anything when you do your fetch:
if (...) {
$file_contents = file_get_contents(...);
// no return call here
} else {
...
return; // return nothing, e.g. null
}
return $result; // $result is NEVER set in your code
You should have return $file_contents. Or better yet:
if (...) {
$result = get cached data
} else {
$result = fetch/get new data
}
return $result;
by using the proper variable names everywhere.
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);
I have an issue I can't seem to find the solution for. I am trying to write to a flat text file. I have echoed all variables out on the screen, verified permissions for the user (www-data) and just for grins set everything in the whole folder to 777 - all to no avail. Worst part is I can call on the same function from another file and it writes. I can't see to find the common thread here.....
function ReplaceAreaInFile($AreaStart, $AreaEnd, $File, $ReplaceWith){
$FileContents = GetFileAsString($File);
$Section = GetAreaFromFile($AreaStart, $AreaEnd, $FileContents, TRUE);
if(isset($Section)){
$SectionTop = $AreaStart."\n";
$SectionTop .= $ReplaceWith;
$NewContents = str_replace($Section, $SectionTop, $FileContents);
if (!$Handle = fopen($File, 'w')) {
return "Cannot open file ($File)";
exit;
}/*
if(!flock($Handle, LOCK_EX | LOCK_NB)) {
echo 'Unable to obtain file lock';
exit(-1);
}*/
if (fwrite($Handle, $NewContents) === FALSE) {
return "Cannot write to file ($File)";
exit;
}else{
return $NewContents;
}
}else{
return "<p align=\"center\">There was an issue saving your settings. Please try again. If the issue persists contact your provider.</p>";
}
}
Try with...
$Handle = fopen($File, 'w');
if ($Handle === false) {
die("Cannot open file ($File)");
}
$written = fwrite($Handle, $NewContents);
if ($written === false) {
die("Invalid arguments - could not write to file ($File)");
}
if ((strlen($NewContents) > 0) && ($written < strlen($NewContents))) {
die("There was a problem writing to $File - $written chars written");
}
fclose($Handle);
echo "Wrote $written bytes to $File\n"; // or log to a file
return $NewContents;
and also check for any problems in the error log. There should be something, assuming you've enabled error logging.
You need to check for number of characters written since in PHP fwrite behaves like this:
After having problems with fwrite() returning 0 in cases where one
would fully expect a return value of false, I took a look at the
source code for php's fwrite() itself. The function will only return
false if you pass in invalid arguments. Any other error, just as a
broken pipe or closed connection, will result in a return value of
less than strlen($string), in most cases 0.
Also, note that you might be writing to a file, but to a different file that you're expecting to write. Absolute paths might help with tracking this.
The final solution I ended up using for this:
function ReplaceAreaInFile($AreaStart, $AreaEnd, $File, $ReplaceWith){
$FileContents = GetFileAsString($File);
$Section = GetAreaFromFile($AreaStart, $AreaEnd, $FileContents, TRUE);
if(isset($Section)){
$SectionTop = $AreaStart."\n";
$SectionTop .= $ReplaceWith;
$NewContents = str_replace($Section, $SectionTop, $FileContents);
return $NewContents;
}else{
return "<p align=\"center\">There was an issue saving your settings.</p>";
}
}
function WriteNewConfigToFile($File2WriteName, $ContentsForFile){
file_put_contents($File2WriteName, $ContentsForFile, LOCK_EX);
}
I did end up using absolute file paths and had to check the permissions on the files. I had to make sure the www-data user in Apache was able to write to the files and was also the user running the script.
In my file read function, if($theData = #fread($fh, filesize($myFile)) was returning false if the target failed contained nothing but a zero, so I added a check for integer (is_numeric). Is this safe?
function readfilecontents($myFile)
{
if($fh = #fopen($myFile, 'r'))
{
$theData = #fread($fh, filesize($myFile));
if($theData || is_numeric($theData))
{
if(#fclose($fh))
{
return $theData;
}
}
}
return false;
}
No, this is not safe - e.g. if you're reading an empty file, your function will return false instead of an empty string. But there is no need for a function like this as file_get_contents() does the exact same thing (just faster).