Jump to content
baser-b

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

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.

Share this post


Link to post
Share on other sites

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?

Share this post


Link to post
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

Edited by Barand
accidental post prior to completion

Share this post


Link to post
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;
   }

 

Edited by Barand
pwd removal

Share this post


Link to post
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];

 

Share this post


Link to post
Share on other sites
3 minutes ago, baser-b said:

Well, I'm not necessarily trying to read ALL the entries from a file.

It might be a good idea to tell us what you are trying to rather than give us code that shows us what you are NOT trying to do.

Share this post


Link to post
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.

Edited by baser-b
Needed to add more info

Share this post


Link to post
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;
}

 

Edited by Barand
Clean up the code

Share this post


Link to post
Share on other sites

Based on your objective you should just import a dictionary and then use code to randomly display the data. I found one Dictionary DB at https://sourceforge.net/projects/mysqlenglishdictionary/

Share this post


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

×

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.