Why does using an If statement change a variable? - php

I am reading a text file and processing some records, a relevant sample of the text file is
#export_dategenre_idapplication_idis_primary
#primaryKey:genre_idapplication_id
#dbTypes:BIGINTINTEGERINTEGERBOOLEAN
#exportMode:FULL
127667880285760063715151750
127667880285760123715151751
I want to perform a specific action when application_id is already stored within my database AND is_primary = 1
I wrote this PHP to test my code:
$fp1 = fopen('genre_application','r');
if (!$fp) {echo 'ERROR: Unable to open file.'; exit;}
while (!feof($fp1)) {
$line = stream_get_line($fp1,128,$eoldelimiter); //use 2048 if very long lines
if ($line[0] === '#') continue; //Skip lines that start with #
$field = explode ($delimiter, $line);
list($export_date, $genre_id, $application_id, $is_primary ) = explode($delimiter, $line);
// does application_id exist?
$application_id = mysql_real_escape_string($application_id);
$query = "SELECT * FROM jos_mt_links WHERE link_id='$application_id';";
$res = mysql_query($query);
if (mysql_num_rows($res) > 0 ) {
echo $application_id . "application id has genre_id" . $genre_id . "with primary of " . $is_primary. "\n";
} else
{
// no, application_id doesn't exist
}
} //close reading of genre_application file
fclose($fp1);
which results in this output on screen and is exactly as I expected.
371515175application id has genre_id6006with primary of 0
371515175application id has genre_id6012with primary of 1
If I then add an IF statement as in the code below, it somehow changes the value of is_primary as shown by the screen display
$fp1 = fopen('genre_application','r');
if (!$fp) {echo 'ERROR: Unable to open file.'; exit;}
while (!feof($fp1)) {
$line = stream_get_line($fp1,128,$eoldelimiter); //use 2048 if very long lines
if ($line[0] === '#') continue; //Skip lines that start with #
$field = explode ($delimiter, $line);
list($export_date, $genre_id, $application_id, $is_primary ) = explode($delimiter, $line);
// does application_id exist?
$application_id = mysql_real_escape_string($application_id);
$query = "SELECT * FROM jos_mt_links WHERE link_id='$application_id';";
$res = mysql_query($query);
if (mysql_num_rows($res) > 0 ) {
if ($is_primary = '1') echo $application_id . "application id has genre_id" . $genre_id . "with primary of " . $is_primary. "\n";
} else
{
// no, application_id doesn't exist
}
} //close reading of genre_application file
fclose($fp1);
?>
The code above results in the following screen display, which incorrectly has the first field with a primary of 1, when as can be seen by the previous screen display and the sample text file it should be 0
371515175application id has genre_id6006with primary of 1
371515175application id has genre_id6012with primary of 1
Can anyone explain what I am doing to make the variable change and how I should use the If correctly please?

You are assigning a value instead of comparing:
($is_primary = '1')
you need
($is_primary == '1')
or === for a type-safe comparison.
This is why some people like to write their comparisons like so:
('1' == $is_primary)
the mistake is impossible to make here because "1" can't be assigned anything.
Personally though, I think that over time and with growing practice, one will learn to spot the mistake.

You need to use:
if ($is_primary == '1')
NOT
if ($is_primary = '1')
Because "=" defines variable and returns true, but "==" actually compares and doesn't change anything.

The if-line has only one = (assign value) instead of two == (compare value).
Try this instead:
if ($is_primary == '1')

if ($is_primary = '1')
You need to use the comparison operator ==, not the assignment operator =

if ($is_primary == '1')
....
NOT = BUT ==
Also consider removing all the stuff that bug the reading of your question for example somthing like:
// does application_id exist?
$res = mysql_query($query);
if (mysql_num_rows($res) > 0 ) {
echo $is_primary. "\n";
}
This is more readable and fit you purpose.
You may even find yourself does kind of bug.

If you type
if($x=10)...
Then you're telling it to attempt the operation
$x=10
to which it will return a success (return true). This will always run the loop, and alter the value of x.
To test $x, use
if($x==10)...
But note that to test for $x not equals 10 is
if($x!=10)...
ie, one equals and one bang.

Chek out PHP comparison operators

Related

How would I modify this fgetcsv to check multiple columns (not just one!)

I have a working script below which checks a column (1) for something and if found, looks at the adjacent column (0) and does some stuff. (i've excluded stuff above the important code but it loops round every file in a directory as $thisGame or thisGameNoExtension which is the same just with no file extension!
$csvFile = fopen("ManualRegionDupes.csv", "r");
while ($csvRows = fgetcsv($csvFile))
{
if ($csvRows[1] == $thisGameNoExtension)
{
$primaryGame = $csvRows[0];
$fileExtension = pathinfo($thisGame, PATHINFO_EXTENSION);
$fileCheck = trim(shell_exec("ls -1 " . escapeshellarg($primaryGame) . "." . escapeshellarg($fileExtension) . " 2>/dev/null"));
if ($fileCheck)
{
echo "\033[00;33m is on the Manual Region Dupes list and primary version detected. Moved to Removed folder\n";
shell_exec("mv " . escapeshellarg($thisGame) . " Removed/");
continue 2;
}
else
{
echo "\033[00;33m is on the Manual Region Dupes list but primary version is missing. Kept.\n";
continue 2;
}
}
}
fclose($csvFile);
I need to create a way of looking at 3 columns (1,2 & 3) not just one. If any of these columns contain what i'm looking for then I want to continue the script. I'm not sure how to acheive this. I've tried:
if ($csvRows[1,2,3] == $thisGameNoExtension)
and also
`if ($csvRows[1] == $thisGameNoExtension) || if ($csvRows[2] == $thisGameNoExtension) || if ($csvRows[3] == $thisGameNoExtension)
But both are incorrect syntax - hopefully they show what i'm trying to acheive though! I'm sure there is a fairly simple solution, apologies for being a dimwit and thankyou in advance for any help you can give me!
Many thanks.
This is pretty basic stuff, but:
if ($csvRows[1] == $thisGameNoExtension || $csvRows[2] == $thisGameNoExtension || $csvRows[3] == $thisGameNoExtension) {
You don't specify the if keyword for each predicate.
Edit: In response to comment, change your if statement to:
if (in_array($thisGameNoExtension, array_slice($csvRows, 0, 3))) {

Matching row data from excel to an array in php

$emapData[3] is row 4 input data in the excel, how would I match $emapData[3] to $categories which is an array. Like if $emapData[3](row 4 excel input , so if user puts AMT 1) is == AMT 1 which exist in $categories array it will insert 5801D447D5583 to the Database which is the value of AMT 1. Also whatever input from the user it will match to categories array it exists. I have tried below.
$filename=$_FILES["file"]["tmp_name"];
if($_FILES["file"]["size"] > 0)
{
$file = fopen($filename, "r");
$count = 0;
$stud_id = strtoupper(uniqid());
$categories=array("5801D447D5583" => "AMT 1","5801D447D5583" => "AMT 2","5801D457CAF7F" => "AMT 3");
while (($emapData = fgetcsv($file, 10000, ",")) !== FALSE)
{
$count++;
$stud_id = strtoupper(uniqid());
$added_by = $_SESSION['userid'];
if($count > 1) {
echo $count;
$sql = "INSERT into students(stud_id ,stud_no , first_name , last_name, date_added ,added_by) values ('$stud_id','$emapData[0]','$emapData[1]', '$emapData[2]' ,NOW(), '$added_by')";
$emapData[3] = $categories['AMT 1'];
$sql1 = "INSERT into categories(student_id ,category_id ) values ('$stud_id','$emapData[3]')";
mysql_query($sql);
}
I would use array search
http://php.net/manual/en/function.array-search.php
array_search — Searches the array for a given value and returns the first corresponding key if successful
So something like
$value = array_search('AMT 1', $categories);
Outputs:
5801D447D5583
So as you said all this
$emapData[3] is row 4 input data in the excel , how would I match $emapData[3] to $categories which is an array. Like if $emapData[3](row 4 excel input , so if user puts AMT 1) is == AMT 1
you would put something like array_search($emapData[3], $categories);
But I would check if it's found by doing
if( $value !== false)
Note- you need to use !== that checks the type as array search can return index 0 in some cases which is false in PHP. IN your particular case you don't have index 0 but it's good practice anyway. If you read the PHP documentation, specifically this warning.
Warning This function may return Boolean FALSE, but may also return a non-Boolean value which evaluates to FALSE. Please read the section on Booleans for more information. Use the === operator for testing the return value of this function.
That is what it is saying.
Cheers.
UPDATE
This line in your code
$emapData[3] = $categories['AMT 1'];
I would replace with at least
$emapData[3] = array_search($emapData[3], $categories);
if(false !== $emapData[3]){
//insert
}else{
//do something else?
}
However there is a lot of other "Stuff" such as using mysql_ family of functions which are removed in PHP7, as well as directly inserting data into your query, which is a security issue, even from a file. It's called SQL Injection.
Personally I wouldn't overwrite $emapData[3] and instead use a separate variable, but I think that is the least of your worries at this point.
But those things are beyond the scope of this question, however I do strongly suggest using something like PDO and prepared statements instead.
UPDATE1
This bit of code
while (($emapData = fgetcsv($file, 10000, ",")) !== FALSE)
{
///..other code
if($count > 1) { //missing closing }
echo $count;
$sql = "INSERT into students(stud_id ,stud_no , first_name , last_name, date_added ,added_by) values ('$stud_id','$emapData[0]','$emapData[1]', '$emapData[2]' ,NOW(), '$added_by')";
$emapData[3] = $categories['AMT 1'];
$sql1 = "INSERT into categories(student_id ,category_id ) values ('$stud_id','$emapData[3]')";
mysql_query($sql);
} //end while
You'll note besides missing the ending } which I pointed out in comments, you never call mysql_query($sql1); so this second insert is never executed.
Ok... So the simple fix is to just add that in like this:
while (($emapData = fgetcsv($file, 10000, ",")) !== FALSE)
{
///..other code
if($count > 1) { //missing closing }
echo $count;
$sql = "INSERT into students(stud_id ,stud_no , first_name , last_name, date_added ,added_by) values ('$stud_id','$emapData[0]','$emapData[1]', '$emapData[2]' ,NOW(), '$added_by')";
mysql_query($sql);
$emapData[3] = $categories['AMT 1'];
$sql1 = "INSERT into categories(student_id ,category_id ) values ('$stud_id','$emapData[3]')";
mysql_query($sql1);
} //end while
You'll notice I moved the first one mysql_query($sql); which is simply for readability sake, you create a query and execute it. So it reads better to create 1 query execute it, then create a second query and execute that, the two things are separate operations independent of one another.
The last bit is I don't know exactly how you dealt with the original issue, but putting that all together should be something like:
while (($emapData = fgetcsv($file, 10000, ",")) !== FALSE)
{
///..other code
if($count > 1) {
echo $count;
$emapData[3] = array_search($emapData[3], $categories);
if(false !== $emapData[3]){
$sql = "INSERT into students(stud_id ,stud_no , first_name , last_name, date_added ,added_by) values ('$stud_id','$emapData[0]','$emapData[1]', '$emapData[2]' ,NOW(), '$added_by')";
mysql_query($sql);
$emapData[3] = $categories['AMT 1'];
$sql1 = "INSERT into categories(student_id ,category_id ) values ('$stud_id','$emapData[3]')";
mysql_query($sql1);
}else{
/*
do something else?
It's a question as I have no way to know the importance of
the array search to the insert, maybe it's ok if it's null.
maybe that is an error that ruins one or both inserts?
So without knowing that, I have no idea on the exact
implementation of non-matching states.
*/
}
}//end if $count
} //end while

PHP - IF-statement false, still runs

The last hour I've been sitting with this problem. I have two if-statements (for testing purposes they are both IF-statements, and not IF- and ELSE IF-statements. The code runs the false IF-statement as if it is true.
The code:
<?php
$sth = $pdo->query("SELECT * FROM myDBTable WHERE alien1='$idkod' OR alien2='$idkod'");
$result = $sth->fetchAll();
if(!$result)
{
echo "No data";
}
else
{
foreach($result as $row)
{
$alien1 = $row['alien1'];
$alien2 = $row['alien2'];
if($idkod == $alien1)
{
echo $idkod . "==" . $alien1;
}
if($idkod == $alien2)
{
echo $idkod . "==" . $alien2;
}
}
}
?>
This will give me the following text on screen:
1234567891234567891234567==1234567891234567891234567
1234567891234567891234567==1234567891234567891234568
Clearly, the second text shouldn't be there, as the statement is not true.
Don't assume anything when making conditional forks, use var_dump() on the variables to temporarily look inside them - that way you best decide how to check for the exact type and value you are expecting.
Then as said already, prefer to check using ===
If you adopt this behaviour you will save countless hours and avoid some quite subtle bugs which can appear in your code.
Having the PHP Truth Tables pinned up for a while will help.
== ignores type when testing for equality. In this case it will assume that both strings are numbers and convert them. This means this will turn into:
9223372036854775807 == 9223372036854775807 //Max int val. Will be different on different systems.
=== will make sure that both arguments are the same type and will not attempt to coerce making
'1234567891234567891234567' === '1234567891234567891234568';
Give the expected result.
PHP equality is wacky sometimes.
It is wrong to use == you need to use === the second is value comparison, the first is object comparison (depending on the context)
I have modified your code a little check it
<?php
$sth = $pdo->query("SELECT * FROM myDBTable WHERE alien1='$idkod' OR alien2='$idkod'");
$result = $sth->fetchAll();
if(!$result)
{
echo "No data";
}
else
{
foreach($result as $row)
{
$alien1 = $row['alien1'];
$alien2 = $row['alien2'];
if($idkod == $alien1 && $idkod != $alien2)
{
echo $idkod . "==" . $alien1;
}
if($idkod == $alien2 && $idkod != $alien1)
{
echo $idkod . "==" . $alien2;
}
}
}
?>

PHP prevent double clean url (improvements?)

For a client at work we have build a website.The website has an offering page which can contain variants of the same type/build, so they ran into problems with double clean-urls.
Just now I wrote a function to prevent that from happening by appending a number to the URL. If thatclean url also exists it counts up.
E.g.
domain.nl/product/machine
domain.nl/product/machine-1
domain.nl/product/machine-2
Updated! return $clean_url; on recursion and on return
The function I wrote works fine, but I was wondering if I have taken the right approach and if it maybe could be improved. Here's the code:
public function prevent_double_cleanurl($cleanurl)
{
// makes sure it doesnt check against itself
if($this->ID!=NULL) $and = " AND product_ID <> ".$this->ID;
$sql = "SELECT product_ID, titel_url FROM " . $this->_table . " WHERE titel_url='".$cleanurl."' " . $and. " LIMIT 1";
$result = $this->query($sql);
// if a matching url is found
if(!empty($result))
{
$url_parts = explode("-", $result[0]['titel_url']);
$last_part = end($url_parts);
// maximum of 2 digits
if((int)$last_part && strlen($last_part)<3)
{
// if a 1 or 2 digit number is found - add to it
array_pop($url_parts);
$cleanurl = implode("-", $url_parts);
(int)$last_part++;
}
else
{
// add a suffix starting at 1
$last_part='1';
}
// recursive check
$cleanurl = $this->prevent_double_cleanurl($cleanurl.'-'.$last_part);
}
return $cleanurl;
}
Depending on the likeliness of a "clean-url" being used multiple times, your approach may not be the best to roll with. Say there was "foo" to "foo-10" you'd be calling the database 10 times.
you also don't seem to sanitize the data you shove into your SQL queries. Are you using mysql_real_escape_string (or its mysqli, PDO, whatever brother)?
Revised code:
public function prevent_double_cleanurl($cleanurl) {
$cleanurl_pattern = '#^(?<base>.*?)(-(?<num>\d+))?$#S';
if (preg_match($cleanurl_pattern, $base, $matches)) {
$base = $matches['base'];
$num = $matches['num'] ? $matches['num'] : 0;
} else {
$base = $cleanurl;
$num = 0;
}
// makes sure it doesnt check against itself
if ($this->ID != null) {
$and = " AND product_ID <> " . $this->ID;
}
$sql = "SELECT product_ID, titel_url FROM " . $this->_table . " WHERE titel_url LIKE '" . $base . "-%' LIMIT 1";
$result = $this->query($sql);
foreach ($result as $row) {
if ($this->ID && $row['product_ID'] == $this->ID) {
// the given cleanurl already has an ID,
// so we better not touch it
return $cleanurl;
}
if (preg_match($cleanurl_pattern, $row['titel_url'], $matches)) {
$_base = $matches['base'];
$_num = $matches['num'] ? $matches['num'] : 0;
} else {
$_base = $row['titel_url'];
$_num = 0;
}
if ($base != $_base) {
// make sure we're not accidentally comparing "foo-123" and "foo-bar-123"
continue;
}
if ($_num > $num) {
$num = $_num;
}
}
// next free number
$num++;
return $base . '-' . $num;
}
I don't know about the possible values for your clean-urls. Last time I did something like this, my base could look like some-article-revision-5. That 5 being part of the actual bullet, not the duplication-index. To distinguish them (and allow the LIKE to filter out false positives) I made the clean-urls look like $base--$num. the double dash could only occur between the base and the duplication-index, making things a bit simpler…
I have no way to test this, so its on you, but here's how I'd do it. I put a ton of comments in there explaining my reasoning and the flow of the code.
Basically, the recursion is unnecessary will result in more database queries than you need.
<?
public function prevent_double_cleanurl($cleanurl)
{
$sql = sprintf("SELECT product_ID, titel_url FROM %s WHERE titel_url LIKE '%s%%'",
$this->_table, $cleanurl);
if($this->ID != NULL){ $sql.= sprintf(" AND product_ID <> %d", $this->ID); }
$results = $this->query($sql);
$suffix = 0;
$baseurl = true;
foreach($results as $row)
{
// Consider the case when we get to the "first" row added to the db:
// For example: $row['titel_url'] == $cleanurl == 'domain.nl/product/machine'
if($row['title_url'] == $cleanurl)
{
$baseurl = false; // The $cleanurl is already in the db, "this" is not a base URL
continue; // Continue with the next iteration of the foreach loop
}
// This could be done using regex, but if this works its fine.
// Make sure to test for the case when you have both of the following pages in your db:
//
// some-hyphenated-page
// some-hyphenated-page-name
//
// You don't want the counters to get mixed up
$url_parts = explode("-", $row['titel_url']);
$last_part = array_pop($url_parts);
$cleanrow = implode("-", $url_parts);
// To get into this block, three things need to be true
// 1. $last_part must be a numeric string (PHP Duck Typing bleh)
// 2. When represented as a string, $last_part must not be longer than 2 digits
// 3. The string passed to this function must match the string resulting from the (n-1)
// leading parts of the result of exploding the table row
if((is_numeric($last_part)) && (strlen($last_part)<=2) && ($cleanrow == $cleanurl))
{
$baseurl = false; // If there are records in the database, the
// passed $cleanurl isn't the first, so it
// will need a suffix
$suffix = max($suffix, (int)$last_part); // After this foreach loop is done, $suffix
// will contain the highest suffix in the
// database we'll need to add 1 to this to
// get the result url
}
}
// If $baseurl is still true, then we never got into the 3-condition block above, so we never
// a matching record in the database -> return the cleanurl that was passed here, no need
// to add a suffix
if($baseurl)
{
return $cleanurl;
}
// At least one database record exists, so we need to add a suffix. The suffix we add will be
// the higgest we found in the database plus 1.
else
{
return sprintf("%s-%d", $cleanurl, ($suffix + 1));
}
}
My solution takes advantage of SQL wildcards (%) to reduce the number of queries from n down to 1.
Make sure that you ensure problematic case I described in lines 14-20 works as expected. Hyphens in the machine name (or whatever it is) could do unexpected things.
I also used sprintf to format the query. Make sure you sanitize any string that is passed through as a string (e.g. $cleanurl).
As #rodneyrehm points out, PHP is very flexible with what it considers a numeric string. You might consider switching out is_numeric() for ctype_digit() and see how that works.

Indexing text files in PHP

I have been set a challenge to create an indexer that takes all words 4 characters or more, and stores them in a database along with how many times the word was used.
I have to run this indexer on 4,000 txt files. Currently, it takes about 12-15 minutes - and I'm wondering if anyone has a suggestion for speeding things up?
Currently I'm placing the words in an array as follows:
// ==============================================================
// === Create an index of all the words in the document
// ==============================================================
function index(){
$this->index = Array();
$this->index_frequency = Array();
$this->original_file = str_replace("\r", " ", $this->original_file);
$this->index = explode(" ", $this->original_file);
// Build new frequency array
foreach($this->index as $key=>$value){
// remove everything except letters
$value = clean_string($value);
if($value == '' || strlen($value) < MIN_CHARS){
continue;
}
if(array_key_exists($value, $this->index_frequency)){
$this->index_frequency[$value] = $this->index_frequency[$value] + 1;
} else{
$this->index_frequency[$value] = 1;
}
}
return $this->index_frequency;
}
I think the biggest bottleneck at the moment is the script to store the words in the database. It needs to add the document to the essays table and then if the word exists in the table just append essayid(frequency of the word) to the field, if the word doesnt exist, then add it...
// ==============================================================
// === Store the word frequencies in the db
// ==============================================================
private function store(){
$index = $this->index();
mysql_query("INSERT INTO essays (checksum, title, total_words) VALUES ('{$this->checksum}', '{$this->original_filename}', '{$this->get_total_words()}')") or die(mysql_error());
$essay_id = mysql_insert_id();
foreach($this->index_frequency as $key=>$value){
$check_word = mysql_result(mysql_query("SELECT COUNT(word) FROM `index` WHERE word = '$key' LIMIT 1"), 0);
$eid_frequency = $essay_id . "(" . $value . ")";
if($check_word == 0){
$save = mysql_query("INSERT INTO `index` (word, essays) VALUES ('$key', '$eid_frequency')");
} else {
$eid_frequency = "," . $eid_frequency;
$save = mysql_query("UPDATE `index` SET essays = CONCAT(essays, '$eid_frequency') WHERE word = '$key' LIMIT 1");
}
}
}
You might consider profiling your app to know exactly where are your bottlenecks. This might give you a better understanding of what can be improved.
Regarding DB optimisation: check if you have an index on word column, then try lowering the number of times you access DB. INSERT ... ON DUPLICATE KEY UPDATE ..., maybe?

Categories