Jump to content

Sending a value returned from file() into a fixed variable?


baser-b

Recommended Posts

So I am trying to write a script that reads a text file that contains words/definitions, prints the word/definition separately, sends those values to an MySQL database, then deletes those same values from the text file without leaving an empty line. I am having some trouble with getting it ALL to go down. I can get it to read/print the info I want, send it to the database, but deleting the values from the file isn't working. It just deletes the entire contents from the file. Here's the code:

   class Vocabulary
   {
      public $date;
      public $word;
      public $definition;
        
      private $file;
      private $list;
      private $output;
   
      function new()
      {
         $date = date("m/d/y");
         $file = "vocab.txt";
         $list = file($file);
         
         foreach ($list as $line)
         {
            $output = explode("-", $line, 2);
			
	    $word = $output[0];
	    $definition = $output[1];
	 }
	 
      queryMysql("INSERT INTO Vocabulary (word, definition, date) VALUES('$word', '$definition', '$date')");

      echo "<br><strong>Word:</strong> " . $word;
      echo "<br><strong>Definition:</strong> " . $definition;
      
      $rewrite = fopen($file, "w+");
      $delete = $output[0] . $output[1];
      
      fwrite($rewrite, str_replace($delete, "", $list));
      fclose($file);
      }
   }
   
   $vocab = new Vocabulary;
   
   echo $vocab->new();

I'm trying to figure out how I can send the values returned from the first "foreach", which would be the only result desired to be returned from the function, into a variable and "set" them. Because from what I believe is happening, its replacing the entire file with "" because its replacing $output[0] .  $output[1] each time until there's no values left. So if I could set the values obtained from the first run of foreach loop then I could specifically delete those values from the file rather than $output[]. I dunno. I'm just beginning with PHP. Thanks.

Link to comment
Share on other sites

Several comments on that code

  • All  the variables in your new() function are local to that function, so why have the class properties defined? In fact, why have a class at all to run a single function?
  • As you have no database connection defined I suspect you are either using mysql_ functions (which are now removed from PHP) or you create a connection for each query (also a no-no).
  • When you are looping through your data you do nothing with that data, except overwriting the values. You need to write to the database within the loop. This bad practice unless using prepared statements (although even this can be slow (more about this later)
  • When storing dates in a db table always use yyyy-mm-dd format (type DATE, DATETIME or TIMESTAMP). In my table I have define the date as TIMESTAMP DEFAULT CURRENT_TIMESTAMP so the date is automatically captured
CREATE TABLE `vocabulary` (
  `word` varchar(50) NOT NULL,
  `definition` varchar(50) DEFAULT NULL,
  `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`word`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Rewriting your class

class Vocabulary
   {
      private $db;
   
      function __construct(PDO $db)                 // create object with a db connection
      {
          $this->db = $db;
      }  
   
      function new($file)
      {
         $list = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
         
         $ins = $this->db->prepare("INSERT INTO vocabulary (word, definition) VALUES (?, ?)");     // prepare INSERT statement

         foreach ($list as $line)
         {
            $output = explode("-", $line);
            
            $ins->execute( $output );
         }
     
      }
   }

$db = pdoConnect('test') // create connection to  test database

$vocab = new Vocabulary($db)   // pass db connection to object
$vocab->new(vocab.txt);

To test, I created a 'vocab.txt' file with 1000 definitions

$vocab = '';
for ($w='a', $i=0; $i<1000; $i++, $w++) {
    $d = "Definition for $w";
    $vocab .= "$w-$d\n";
}
file_put_contents('vocab.txt', $vocab);

The time taken was 45.539 seconds

A second version of the new function wrote all the definitions in a single query in the form

Quote

INSERT INTO vocabulary (word, definition) VALUES ('word1',  'def1'), ('word2', 'def2'), ... , ('word1000', 'def1000')

This code was

      function new2($file)
      {
         $list = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
         
         $params = $placeholders = [];
         foreach ($list as $line)
         {
            $output = explode("-", $line);
            $placeholders[] = "(?,?)";
            array_push($params, $output[0], $output[1]);
         }
     
         $ins = $this->db->prepare("INSERT INTO vocabulary (word, definition) VALUES " . join(',', $placeholders));     // prepare INSERT statement
         $ins->execute( $params );

      }

The time taken using this version was 0.266 seconds

Link to comment
Share on other sites

Well one about the why have a class for a single function, I am going to build upon the class that's just the one part I have a question about.

A lot of the things in my code that don't make sense is because I am new at PHP and programming in general, and have not had more of an education than w3schools.com and reading other people's code and referencing it with php.net/manual/.

I do have database functions defined but they are not directly relevant to the problem at hand, so I didn't include it. It is here:

   // Define MySQL variables

   define("SERVER", "localhost");
   define("USERNAME", "admin");
   define("PASSWORD", "??????");
   define("DATABASE", "homepage");
   
   // Connect to MySQL Database
   
   $connection = new mysqli(SERVER, USERNAME, PASSWORD, DATABASE);
   
   if ($connection->connect_error)
   {
      die($connection->connect_error);
   }
   
   function queryMysql($query)
   {
      global $connection;
      $result = $connection->query($query);

      if (!$result) die($connection->error);
      return $result;
   }

 

Link to comment
Share on other sites

7 hours ago, requinix said:

But, if you're reading all the lines from the file, then deleting all of those entries from the file, won't the end result be an empty file anyways?

Well, I'm not necessarily trying to read ALL the entries from a file. My knowledge is limited. I know that file() turns a file into an array, but then I didn't know how to read a single line from the array produced, and now having thought it through, I figured that out, but it was a former experiment that had confused me when the $output[0] just returned a letter or something. But anyway this code does the same thing without getting the set of values I want from the array....

 $file = "vocab.txt";
 $source = file($file);
 
 $entry = explode("-", $source[0]);
 $word = $entry[0];
 $definition = $entry[1];

 

Link to comment
Share on other sites

Thanks for the edit, that wasn't my password though. Anyway, as an edit to my previous post: I am basically creating a part of a homepage script that will give me a vocabulary "word of the day" + definition as part of the overall setup. The point of the function I am trying to create is to serve 4 purposes:

  • Read a random line from a text file containing a word and definition. Separate the two values.
  • Print the two values as Word: $word and Definition: $definition.
  • Store the values into an MySQL database that can be retrieved at another time.
  • Delete the values from the text file without leaving a blank line

I like learning new words and usually will put it in a textfile when I see it and get the definition later. Part of the purpose of the function is to specifically give me a word to meditate on each day and maybe also add a "memory game" or something on the page to help learn the word/definition rather than just accumulating words in a textfile.

Link to comment
Share on other sites

Here's one way

$db = pdoConnect('test');

class Vocabulary
   {
      private $db;
   
      function __construct(PDO $db)                 // create object with a db connection
      {
          $this->db = $db;
      }  
   
      function new($file)
      {
         $list = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
         
         $rand_index = array_rand($list);
         
         $wotd =  $list[$rand_index];                // get the word of the day
         list($word, $def) = explode('-', $wotd);
         
         $ins = $this->db->prepare("INSERT INTO vocabulary (word, definition, date) VALUES (?, ?, CURDATE() )");     // prepare INSERT statement

         $ins->execute( [$word, $def] );

         unset($list[$rand_index]);   // remove selected word def
         $text = '';
         foreach ($list as $line) {
             $text .= $line . "\n" ;
         }
         file_put_contents($file, $text);    // rewrite the file
         
         return [$word, $def];
      }
   }      

$vocab = new Vocabulary($db);

list($word, $definition) = $vocab->new('vocab.txt');

echo "Word of the day is <b>$word</b><br>Definition: <i>$definition</i>";

 

My 0.02 worth:

Avoid using globals, it's too easy to inadvertently change a value that then has hidden repercussions throughout your code. Pass values as function arguments instead.

As you are still learning, switch to PDO instead of mysqli, it's easier. Mysqli creates a different type of object depending on whether you use query() or prepare(). These objects have different methods to process what is essentially the same results (and some methods only work depending on your php setup). With pdo you have the one set of methods.

If you do use mysqli, call

mysqli_report(MYSQLI_REPORT_ERROR|MYSQLI_REPORT_STRICT);

before you connect. This will save you from having to check if every operation was successful as it will automatically throw an error.

With PDO, use the options when connectiong EG

function pdoConnect($database) 
{
    $dsn = "mysql:dbname=$database; host=".HOST."; charset=utf8";

    $db = new pdo($dsn, USERNAME, PASSWORD, 
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_EMULATE_PREPARES => false,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    return $db;
}

 

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.