Page 1 of 1

[SOLVED] Troubles with old string replacement and foreach...

Posted: Tue Jun 29, 2004 7:27 pm
by tomfra
I am trying to make a php script that will rename files specified in a MySQL database to random names each time the script is executed and if the old filenames were mentioned in those files, it will replace any occurrences of the old string with the new string.

The db table has this structure:

Code: Select all

CREATE TABLE `filenames` (
  `UID` int(10) unsigned NOT NULL auto_increment,
  `orig_name` varchar(100) NOT NULL default '',
  `new_name` varchar(100) NOT NULL default '',
  `temp_name` varchar(100) NOT NULL default '',
  PRIMARY KEY  (`UID`),
  KEY `new_name` (`new_name`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
Nothing fancy but good enough for what needs to be done.

Almost everything is working just fine... except for one "foreach" cycle. Take a look at the below code. It's the complete script (it's far from finished though):

Code: Select all

<?php

$db_host = "localhost";
$db_user = "xxx";
$db_pass = "xxx";
$db_name = "xxx";

function rand_name() {
  $rand_name = '';
    for ($i = 0; $i < 8; $i++){     
    $chars = array('a','b','c','d','e','f','g','h','i','j','k',
    'l','m','n','o','p','q','r','s','t','u','v','w','x','y','z');
    $rand_name .= $chars[rand(0, count($chars)-1)];
  }
  return $rand_name;
}

function db_result_to_array($query_result, $array_name) {
   for ($count=0; $row = mysql_fetch_array($query_result); $count++) { 
        $res_array[$count] = $row[$array_name]; 
      } 
  return $res_array;
}

$link = mysql_connect("$db_host", "$db_user", "$db_pass") or die
  ('Could not connect to MySQL server.<br>' . mysql_error());
  mysql_select_db("$db_name") or die;

$query = "SELECT * FROM `filenames`";
$result = mysql_query($query);
$rows = mysql_num_rows($result);
$row = mysql_fetch_array(mysql_query($query));
$orig_name = $row["orig_name"];
$new_name = $row["new_name"];
$UID = $row["UID"];

// Update to new names code start

if ($com == 'back') { //add ?com=back to the script URL to return back to original names
  update($query, $result, $echo, $back);
  }else{
  update($query, $result, $echo);  
}

function update($query, $result, $echo = false, $back = false){
 $query_result = mysql_query($query);
 $query_result1 = mysql_query($query);
  while ($row = mysql_fetch_array($result)) {
 
	 $orig_name = $row["orig_name"];
	 $new_name = $row["new_name"];
	 $temp_name = $row["temp_name"];
	 $UID = $row["UID"];
	 
  if (is_null($back)){
    $rand_name = $orig_name;
    }else{
    $rand_name = rand_name()."$UID".".".array_pop(explode('.', $orig_name));}
 
  mysql_query("UPDATE `filenames` SET `temp_name`='$new_name'
               WHERE `orig_name`='$orig_name'"); 
  
  mysql_query("UPDATE `filenames` SET `new_name`='$rand_name'
               WHERE `orig_name`='$orig_name'");
  
  // String replace & rename files code start \\

  if (file_exists($new_name)) {
    
   $query_result = mysql_query($query);
   $temp_name_array = db_result_to_array($query_result, 'temp_name');
   
   $query_result1 = mysql_query($query);
   $new_name_array = db_result_to_array($query_result1, 'new_name');
    
  // The foreach cycle below is the problematic part that needs to be fixed \\
    foreach ($new_name_array AS $value){
      $source = str_replace ($temp_name_array, $new_name_array,
                file_get_contents($new_name));
      }
             
    file_put_contents($new_name, $source); // file_get_contents & file_put_contents is
    rename ($new_name, $rand_name);       // defined in external file for PHP < 5.0
  }  
  
  // String replace & rename files code stop \\

  if (is_null($echo)){	
		echo "<table align='center' width='300'><tr>
  <td width='150'>$orig_name</td>
  <td width='150'>$rand_name</td></tr></table>";
} } }
// Update to new names code stop

?>
Renaming files works just great without a glitch. The string replacement within the files is a problem though. Let's say there are 10 files to be replaced, all of them specified in the database. Their $orig_name, $new_name and $temp_name is initially the same so the rows look like this:

orig_name new_name temp_name

filename1.ext filename1.ext filename1.ext
filename2.ext filename2.ext filename2.ext
filename3.ext filename3.ext filename3.ext
...and so on

When you run the script, it replaces $new_name with a random name. When you run it again, it does it again and the old random name is now in the $temp_name value / db row. This is all actually working.

But when it should replace string "filename3.ext" in the body of filename2.ext, it does not because filename3.ext is in the database after filename2.ext and it doesn't replace anything after it because the foreach cycle does not know the new value yet.

Hopefully you understand what I am trying to explain. I still consider myself a newbie so I am quite sure I made a very simple mistake somewhere but just can't find it.

Any help is appreciated!

Regards,

Tomas

Posted: Wed Jun 30, 2004 8:29 am
by lostboy
generate all the new names first, then do the search and replace

Posted: Wed Jun 30, 2004 9:41 am
by tomfra
Bastien,

I think I did that but apparently there is a mistake somewhere because the foreach cycle does not know the correct new names. I know it is all about "timing" but I am still too much just a newbie to find where the problem is exactly. By the time the foreach cycle starts, the new names are / should be in the database and the $new_name_array but they are not and I can't find how to correct the timing.

I agree with your advice but the question is "How to accomplish it"?

Tomas

Posted: Wed Jun 30, 2004 11:03 am
by DaiWelsh
Tomas, you need to split the name generation code and the search/replace code into two seperate loops through the db, so that all the anmes are generated before the search/replace begins. e.g. (untested)

Code: Select all

<?php 
function update($query, $result, $echo = false, $back = false){ 
  while ($row = mysql_fetch_array($result)) { 
    $orig_name = $row["orig_name"]; 
    $new_name = $row["new_name"]; 
    $temp_name = $row["temp_name"]; 
    $UID = $row["UID"]; 
    if (is_null($back)){ 
      $rand_name = $orig_name; 
    }else{ 
      $rand_name = rand_name()."$UID".".".array_pop(explode('.', $orig_name));
    } 
    mysql_query("UPDATE `filenames` SET `temp_name`='$new_name',`new_name`='$rand_name'  
               WHERE `orig_name`='$orig_name'"); 
  }
// now that all name generation is finished 'rewind' the query and start the rennaming
  mysql_data_seek($result,0);
  // get these arrays once rather than every time through loop
  $query_result = mysql_query($query); 
  $temp_name_array = db_result_to_array($query_result, 'temp_name'); 
  $new_name_array = db_result_to_array($query_result, 'new_name');  
  while ($row = mysql_fetch_array($result)) { 
    $orig_name = $row["orig_name"]; 
    $new_name = $row["new_name"]; 
    $temp_name = $row["temp_name"]; 
    // String replace & rename files code start \\ 
    if (file_exists($temp_name)) { 
      foreach ($new_name_array AS $value){ 
        $source = str_replace ($temp_name_array, $new_name_array, 
                file_get_contents($temp_name)); 
      } 
      file_put_contents($temp_name, $source);
      rename ($temp_name, $new_name); 
    }  
    if (is_null($echo)){    
      echo "<table align='center' width='300'><tr> 
    <td width='150'>$orig_name</td> 
    <td width='150'>$new_name</td></tr></table>"; 
    }
  }
} 
// Update to new names code stop 
?>

Posted: Wed Jun 30, 2004 1:57 pm
by tomfra
DaiWelsh,

Thanks for the code! With some modifications the search & replace part now works just fine... but the rename files part does not.

Here is the modified update function:

Code: Select all

function update($query, $result, $echo = false, $back = false){
  while ($row = mysql_fetch_array($result)) {
 	 $orig_name = $row["orig_name"];
	 $new_name = $row["new_name"];
	 $temp_name = $row["temp_name"];
	 $UID = $row["UID"];
	 
  if (is_null($back)){
    $rand_name = $orig_name;
    }else{
    $rand_name = rand_name()."$UID".".".array_pop(explode('.', $orig_name));}
 
  mysql_query("UPDATE `par_filenames` SET `temp_name`='$new_name',
              `new_name`='$rand_name' WHERE `orig_name`='$orig_name'"); 
  }
  
  // Escaped from the while loop code start \\

  mysql_data_seek($result,0); 

  $query_result = mysql_query($query);
  $query_result1 = mysql_query($query);
  
  $temp_name_array = db_result_to_array($query_result, 'temp_name'); 
  $new_name_array = db_result_to_array($query_result1, 'new_name');
  
  // Escaped from the while loop code stop \\

  while ($row = mysql_fetch_array($result)) {
 	 $orig_name = $row["orig_name"];
	 $new_name = $row["new_name"];
	 $temp_name = $row["temp_name"];
  
  // String replace & rename files code start \\

  if (file_exists($temp_name)) {
    foreach ($new_name_array AS $value){
      $source = str_replace ($temp_name_array, $new_name_array,
                file_get_contents($new_name));
      }      
     file_put_contents($temp_name, $source);  
     rename ($temp_name, $new_name); 
  }  
  
  // String replace & rename files code stop \\

  if (is_null($echo)){	
		echo "<table align='center' width='300'><tr>
  <td width='150'>$orig_name</td>
  <td width='150'>$new_name</td></tr></table>";
} } }
// Update to new names code stop
The problem seems to be that the new value for $new_name is something that is not known to the part of the script in the second while loop - it seems to be one cycle behind in updating.

Any idea? The real problem is that while & foreach loops is something I started playing with less than a week ago so it's very likely I simply don't see an obvious bug.

Thanks!

Tomas

Posted: Thu Jul 01, 2004 6:11 am
by tomfra
Nobody knows how to fix the rename files loop? I am sure I will find it myself... in a month or so ;) Really folks, if you know how to fix it, don't be shy and tell me :)

Thanks!

Tomas

Posted: Fri Jul 02, 2004 3:44 pm
by lostboy
Can I ask what exactly are you trying to accomplish. Not the renaming as that is clear enough, but what is the business logic behind doing this?

Posted: Fri Jul 02, 2004 4:52 pm
by tomfra
I've already found the problem - it was because the two while loops were using the same condition - i.e. $row = mysql_fetch_array($result). When I changed the second one to $row = mysql_fetch_array($result1) (and specified $result1 of course) it began to work.

The business logic? There are more reasons but let's say it's for some specific security reasons. If the potential hackers do not know what the filename of the file they are trying to access is, then their change to do anything bad is significantly lower. And if you are using it on scripts that need to reference the filenames of the other files in order to work, then it must do the search & replace part according to the new filenames.

This is not the only reason though.

Tomas