Jump to content

[PHP] end of file infinite loop

CJPowell27

Hello everyone, I have a weird issue where my feof loop is not terminating when it should. This should be processing media from a 3 line file and runs infinitely. can anyone spot why it would be doing this?

<!doctype html>
<?php
require('mlib_functions.php');
html_head("mlib skeleton");
require('mlib_header.php');
require('mlib_sidebar.php');

# Code for your web page follows.
echo "<h2>Processing Equipment from List...</h2>";

if ($_FILES['userfile']['error'] > 0)
{
  echo 'Problem: ';
  switch ($_FILES['userfile']['error'])
  {
    case 1:  echo 'File exceeded upload_max_filesize';  break;
    case 2:  echo 'File exceeded max_file_size';  break;
    case 3:  echo 'File only partially uploaded';  break;
    case 4:  echo 'No file uploaded';  break;
  }
  exit;
}

// Does the file have the right MIME type?
if ($_FILES['userfile']['type'] != 'text/plain')
{
  echo 'Problem: file is not plain text';
  exit;
}

// put the file where we'd like it
$upfile = './uploads/'.$_FILES['userfile']['name'];

if (is_uploaded_file($_FILES['userfile']['tmp_name'])) 
{
  if (!move_uploaded_file($_FILES['userfile']['tmp_name'], $upfile))
  {
    echo 'Problem: Could not move file to destination directory';
    exit;
  }
} 
else 
{
  echo 'Problem: Possible file upload attack. Filename: ';
  echo $_FILES['userfile']['name'];
  exit;
}

echo 'File uploaded successfully<br><br>'; 

// display the file contents
$fp = fopen($upfile, 'r');

if(!$fp)
{
    echo "<p> I could not open $upfile right now</p>";
    exit;
}

#Process file one line at a time
while(!feof($fp,100))
{
    $line = fgets($fp,160);
    $line_array = explode(',',$line);
    $title1 = trim($line_array[0]);
    $author1 = trim($line_array[1]);
    $type1 = trim($line_array[2]);
    $description1 = trim($line_array[3]);
    $errors = validate_media($title1, $author1, $type1, $description1);
    if(empty($errors))
    {
        #Display uploaded entries
        echo "Title: $title1</br>";
        echo "Author: $author1 </br>";
        echo "Type: $type1</br>";
        echo "Description: $description1</br>"; 
        try
        {
            $db = new PDO(DB_PATH,DB_LOGIN,DB_PW);
            $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $db->exec("INSERT INTO media(title,author,description,type) VALUES('$title1','$author1','$description1','$type1');");
        }
        catch(PDOException $e)
        {
            echo 'Exception: '.$e->getMessage();
            echo "<br/>";
            $db = NULL;
        }
    }
    else
    {
        
        echo "Errors found in media entry: <br/>";
        echo "Title: $title1 <br/>";
        echo "Type: $type1<br/>";
        echo "Description: $description1 <br/>";
        foreach($errors as $error)
        {
            echo $error."<br/>";
        }
        echo "<br/>";
    }
}

#Close the file
flose($fp);
require('mlib_footer.php');

?>

the relevant code in mlib_functions is

Spoiler

function validate_media($title1, $author1,$type1, $description1) {
  $error_messages = array(); # Create empty error_messages array.
  if ( strlen($title1)  == 0 ) {
    array_push($error_messages, "title field must have a title name.");
  }

  if ( strlen($type1) == 0 ) {
    array_push($error_messages, "Type field must have a type.");
  }

  if ( strlen($description1) == 0 ) {
    array_push($error_messages, "Description field must have a description.");
  }

  try {
    //open the database
    $db = new PDO(DB_PATH, DB_LOGIN, DB_PW);
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    //check for invalid type
    if ( strlen($type1) != 0 ) {
      $sql = "SELECT COUNT(*) FROM mlib_types WHERE type = '$type1'";
      $result = $db->query($sql)->fetch(); //count the number of entries with the same name
      if ( $result[0] == 0) {
        array_push($error_messages, "type $type1 is not defined. type must be valid.");
      }
    }
      
    //check for duplicate title name
    if ( strlen($title) != 0 ) {
      $sql = "SELECT COUNT(*) FROM media WHERE title = '$title1'";
      $result = $db->query($sql)->fetch(); //count the number of entries with the title name
      if ( $result[0] > 0) {
        array_push($error_messages, "$title1 is not unique. title names must be unique.");
      }
    }
  }

  catch(PDOException $e){
    echo 'Exception : '.$e->getMessage();
    echo "<br/>";
    $db = NULL;
  }

  return $error_messages;
}

 

 

i5 4670k| Asrock H81M-ITX| EVGA Nex 650g| WD Black 500Gb| H100 with SP120s| ASUS Matrix 7970 Platinum (just sold)| Patriot Venom 1600Mhz 8Gb| Bitfenix Prodigy. Build log in progress 

Build Log here: http://linustechtips.com/main/topic/119926-yin-yang-prodigy-update-2-26-14/

Link to comment
Share on other sites

Link to post
Share on other sites

while(!feof($fp,100))

What's the 100 for?

 

Anyway why not just

while($line = fgets($fp,160))
{
	*whatever*
}

?

Don't ask to ask, just ask... please 🤨

sudo chmod -R 000 /*

Link to comment
Share on other sites

Link to post
Share on other sites

9 minutes ago, Sauron said:


while(!feof($fp,100))

What's the 100 for?

 

Anyway why not just



while($line = fgets($fp,160))
{
	*whatever*
}

?

That's what my professor wanted for some reason but I suppose he would prefer it working lol. I'll implement that for the time being. Much of what I've read warns against using !feof so I'm not sure his goal.

i5 4670k| Asrock H81M-ITX| EVGA Nex 650g| WD Black 500Gb| H100 with SP120s| ASUS Matrix 7970 Platinum (just sold)| Patriot Venom 1600Mhz 8Gb| Bitfenix Prodigy. Build log in progress 

Build Log here: http://linustechtips.com/main/topic/119926-yin-yang-prodigy-update-2-26-14/

Link to comment
Share on other sites

Link to post
Share on other sites

Just as a general debugging tip, create a separate script(s) to test specific aspects of your code, isolated from the rest of your code.  In your example, since the loop seems to be the issue, I would suggest creating a separate script, where the ONLY thing it does is read in each line of the file, and then output it for you to see.

Work on that one aspect until you get it working, then integrate your solution back into your main project.

 

 

 

Link to comment
Share on other sites

Link to post
Share on other sites

There's some problems with the code.

 

feof  returns TRUE if it's end of file or there was an error reading a file , FALSE otherwise.

fgets tries reading from the current position UP TO 160 -1 bytes (or whatever value you set, minus 1 ) or until a NEWLINE is detected, but INCLUDES the newline character in the string, and you don't really want that.

Also, in your code, you will have a problem if one line of text actually is bigger than 159 characters (which could be if you have artists or titles in a foreign language, using UTF-8 characters, where one character may use 2-3 bytes.

 

If the teacher does not require you to read the file one line at a time, and you know the files will be small enough that there won't be memory limitations, it's best to read the whole file in memory and then read it one line at a time.

Also, it's a good idea to use bigger buffers, 512 bytes or 4096 bytes are common values and what the underlying file system and operating system would us either way (so you'll have the same performance).

 

After the whole file is read in memory, you can split it in lines by looking for the invisible characters that form the ENTER key.

There's actually two characters

1. CR or "\r"  ( ascii code 13 or 0x0D in hexadecimal) - carriage return, this means move the cursor back to beginning of line

2. LF or "\n"  (ascii code 10 or 0x0A in hexadecimal) - line feed,  advance the cursor to next line

 

Windows likes to insert both bytes, CRLF or  "\r\n"  , Linux likes to use only the "\n" character. Some Macs only used to insert the "\r" character.

 

Now that you know this, you may spot your first problem with the fgets function - it only stops when the newline "\n" character is found and includes in the string all characters before it. If you write the text file in Windows where the enter key has both \r and \n , then the last character in your line of text will be that \r character.

 

As a proof, you can write this php code in a page, run it and see the result :

 

<?php
// put john ENTER smith into a string variable
$text = "john\r\nsmith";
// save the string into a filename called test.txt, in the same folder holding our script
$result = file_put_contents(__DIR__ .'/test.txt',$text);
// now let's open the file using fopen
$fileHandle = fopen(__DIR__ .'/test.txt','r');
// read up to 100 bytes, or until \n byte is found
$textLine = fgets($fileHandle,100);
// close the file handle (it will be auto closed when script ends anyway, but better to learn)
$result = fclose($fileHandle);
// print the contents of the $textLine variable
var_dump($textLine);
?>

 

Now if you look at the output you will see:

 

string(6) "john
"

 

Anyway, so it would be best to read the whole file, and then wherever you have a \r  or a \n character (because depending on what system the file was created, you may have only one or both of those)

 

You could use the function file_get_contents($filename) but I suspect the teacher wants you to learn those other functions, so let's use fread :

 

$fileName = __DIR__ .'/test.txt';
// this variable will hold the contents of the whole file
$fileContent= ''; 
// open file
$fileHandle = fopen($fileName,'r');
if ($fileHandle !== FALSE) {
	// we just opened the file, so we don't know if  we're at the end of it
	// set our variable to false to attempt to read at least once from the file
	$fileEnd = FALSE;
	while ($fileEnd == FALSE) {
		// read up to 4096 bytes and append them to the fileContent variable
		$buffer = fread($fileHandle,4096);
		$fileContent .= $buffer;
		// check if we reached the end of file
		$fileEnd = feof($fileHandle);
		// if not the end, loop back to beginning of while
	}
	$result = fclose($fileHandle);
	// to keep things simple we simply replace any \r with \n
	// ENTER becomes \n\n, so we'll have an empty line 
	// \r becomes \n, a proper line separator
	// \n remains \n 
	$fileContent = str_replace("\r","\n",$fileContent);
	// now we can use explode functions to split into lines
	$fileLines = explode("\n",$fileContent);
	// we can use foreach to loop through the lines put into array
	foreach ($fileLines as $lineCounter => $fileLine) {
		// lineCounter is the position in the array
		// fileLine is the actual text line
		// if the file used ENTER, then we will have empty lines because 
		// we had two consecutive \n characters, as we replaced \r with \r\n
		// let's test and ignore empty lines or lines that have only spaces
		$fileLineClean = trim($fileLine);
		if ($fileLineClean!='') {
			// this line actually has contents we care about
			$line_array = explode(',',$fileLineClean);
			foreach ($line_array as $i => $s) {
				$line_array[$i] = trim($s);
			}
			$title1 = $line_array[0];
			$author1 = $line_array[1];
			$type1 = $line_array[2];
			$description1 = $line_array[3];
			// you could also use list() , which puts the data from array into your variables
			list($title1,$author1,$type1,$description1) = $line_array;
			// let's see the four variables
			var_dump($title1,$author1,$type1,$description1);
			// do whatever you want with it.
			//$errors = validate_media($title1, $author1, $type1, $description1);
			// .... 
		}
	}
} else {
	die('could not open the file');
}

 

Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×