Check email against a database

I’m trying to take emails entered and check them against a database. If one already exists, finish carrying on the function but do not insert and notify the user. Here’s my test file:

<?php

require_once('class.database.php');

try {
	$objDB = Database::instance();
} catch (Exception $e) {
	echo $e->getMessage();
	exit(2);
}

try {
	$table = "users";	
	$objDB->insert($table, array('email' => 'me@example.com', 'password' => 'foo'));
	$objDB->insert($table, array('email' => 'mean@example.com', 'password' => 'bar'));
	$objDB->insert($table, array('email' => 'meanme@example.com', 'password' => 'foobar'));
	$data = $objDB->select("SELECT * FROM users");

	var_dump($data);
} catch (Exception $e) {
	echo "Query failure" . NL;
	echo $e->getMessage();
}

?>

And here’s the two functions:

		function isValidEmail($field) {
			$stmt = "SELECT email FROM users WHERE email = '$field'";
			$result = mysql_query($stmt);

			if(!(mysql_num_rows($result))) {
				//duplicate 
				echo "<div id='duplicate' class='error'>NOT A VALID EMAIL: DUPLICATE: EMAIL EXISTS IN DATABASE<div>";
				return(false);
			};
			return(true);

		}

		function insert($tableName, $arValues) {
			$id = null;
			$sFieldList = join(', ', array_keys($arValues));
			$arValueList = array();
			
			foreach($arValues as $value) {
				if(strtolower($value) == '#id#') {
					//we need to get the next value from this tables sequence
					$value = $id = $this->conn->nextID($tableName . "_id");
				}
				$arValueList[] = $this->conn->quoteSmart($value);
				$validEmail = $this->isValidEmail($arValueList);
			}
			
			$sValueList = implode(', ', $arValueList);
			
			//make sure the table name is properly escaped
			$tableName = $this->conn->quoteIdentifier($tableName);
			
			$sql = "INSERT INTO $tableName  ($sFieldList) VALUES ($sValueList)";
			//Only INSERT if there are no duplicates
			//If one exists, the Message is shown in function isValidEmail
			if($validEmail) {
				$result = $this->conn->query($sql);
			}

			if(DB::isError($result)) {
				throw new Exception($result->getMessage(), $result->getCode());
			}
			
			//Return the ID if there was one, or return the number of affected rows
			return $id ? $id : $this->conn->affectedRows();
		}

It works to completion, just either inserts or doesn’t. I can’t make it differentiate whether the email exists or it doesn’t.

if(!(mysql_num_rows($result))) {

doesn’t Insert anything and:

if((mysql_num_rows($result))) {

inserts everything.

I have my logic wrong somewhere. Any help is appreciated.

$arValueList = $this->conn->quoteSmart($value);
$validEmail = $this->isValidEmail($arValueList);

So you’re passing it in as an Array.
function isValidEmail($field) {
$stmt = “SELECT email FROM users WHERE email = ‘$field’”;

and then trying to use it as a String. Not gonna work that way. Get rid of the array markers on that $arValueList declaration and give it a whirl with the Not num_rows

I would something like this

 
$email = strtolower($_POST['txtEmail']);
 
$query = 'select * from tblUsers where fldEmail = "'.$email.'"';
 
$rs = mysql_query($query,$conn);
 
if(!$rs) die("<p>Could not check the database</p>");
 
if(mysql_num_rows($rs) == 1) {
 
     //email already exists
 
} else {
 
    //email does not exist
}
 

Side note: Your test file doesnt actually generate a collision… might want to actually try and generate one (insert the same email twice).

The emails already exist in the table…I’ll try your suggestion here in a bit. Thanks

Alright…I’m trying real hard here and it’s escaping me. I used implode to move the array to a string like suggested. I honestly can’t find a way to make this work. Here’s what I’ve got so far. I’m doing my best to learn this entirely, so please bare with me.

		function isValidEmail($field) {
			$sField = implode(', ', $field);
			$stmt = "SELECT email FROM users WHERE email = '$sField'";
			$result = mysql_query($stmt);

Why is $result empty? The email i’m testing with exist in the table. If I try to use mysql_num_rows($result); I get a warning saying it expects the param to be a resource and not boolen. Fair enough. I don’t know how to make that work. Any help?

Moving on:

		if(!$result) {
				print "<p>Error: Problem with query</p>";
			} else {
				$row = mysql_num_rows($result);
			};

This spits out the Error msg. Obviously telling me result isn’t empty but is indeed true because it’s not empty. Right? Doing mysql_num_rows this way doesn’t cause the error. Why?

Moving On:

		if($row == 0) {
				var_dump($row);
				print "EMAIL VALID";
				return(true);
			} else {
				//duplicate 
				echo "<div id='duplicate' class='error'>NOT A VALID EMAIL: DUPLICATE: EMAIL EXISTS IN DATABASE<div>";
				return(false);
			};
			print "Should Not Reach This Point!<BR><BR>";
			return(false);
		}

So if $result was not empty and true (if I understand correctly) then $row should not be 0 and the email exists in the database, return false. Email isn’t valid and don’t submit it. Now I change the email in my test script to something that doesn’t exist in the table and it still fails. This tells me it’s the query. Right? So I put in the

print "<p>Error: Problem with query</p>";

Sure enough it’s the Query. What’s wrong with my statement?

All code I’m using:

		function isValidEmail($field) {
			$sField = implode(', ', $field);
			$stmt = "SELECT email FROM users WHERE email = '$sField'";
			$result = mysql_query($stmt);
			if(!$result) {
				print "<p>Error: Problem with query</p>";
			} else {
				$row = mysql_num_rows($result);
			};

			if($row == 0) {
				var_dump($row);
				print "EMAIL VALID";
				return(true);
			} else {
				//duplicate 
				echo "<div id='duplicate' class='error'>NOT A VALID EMAIL: DUPLICATE: EMAIL EXISTS IN DATABASE<div>";
				return(false);
			};
			print "Should Not Reach This Point!<BR><BR>";
			return(false);
		}

		function insert($tableName, $arValues) {
			$id = null;
			$sFieldList = join(', ', array_keys($arValues));
			$arValueList = array();
			
			foreach($arValues as $value) {
				if(strtolower($value) == '#id#') {
					//we need to get the next value from this tables sequence
					$value = $id = $this->conn->nextID($tableName . "_id");
				}
				$arValueList[] = $this->conn->quoteSmart($value);
				$validEmail = $this->isValidEmail($arValueList);
			}
			
			$sValueList = implode(', ', $arValueList);
			
			//make sure the table name is properly escaped
			$tableName = $this->conn->quoteIdentifier($tableName);
			
			$sql = "INSERT INTO $tableName  ($sFieldList) VALUES ($sValueList)";
			//Only INSERT if there are no duplicates
			//If one exists, the Message is shown in function isValidEmail
			if($validEmail) {
				$result = $this->conn->query($sql);
			}

			if(DB::isError($result)) {
				throw new Exception($result->getMessage(), $result->getCode());
			}
			
			//Return the ID if there was one, or return the number of affected rows
			return $id ? $id : $this->conn->affectedRows();
		}

Test Script: (I change the email to something that doesn’t exist once It’s valid and entered):

require_once('class.database.php');

try {
	$objDB = Database::instance();
} catch (Exception $e) {
	echo $e->getMessage();
	exit(2);
}

try {
	$table = "users";	
	$objDB->insert($table, array('email' => 'eme@example.com', 'password' => 'foo'));

	$data = $objDB->select("SELECT * FROM users");
} catch (Exception $e) {
	echo "Query failure" . NL;
	echo $e->getMessage();
}

I feel like I’m close but who knows. I am trying my best. If there is a better OO way to write this please suggest :). I’m not wanting someone to write this for me. I’m trying to learn so documentation or points in the right direction are very welcome. Thanks for your time.

I’m not good at multi-tasking :weyes: so I’ll try to help on this issue as a start.

  1. make sure your $stmt is evaluating correctly.

add the red line

 
$stmt = "SELECT email FROM users WHERE email = '$sField'";
 
[COLOR=red]echo $stmt; die();[/COLOR]

if the echoed $stmt is correct then you are not connected to the database when you run

 
$result = mysql_query($stmt);

and so $result will evaluate to false (a boolean)

SELECT email FROM users WHERE email = ''eme@example.com''

is the output of:

echo $stmt; die();

This means $stmt is a string. But the " " around the email address is the problem correct? This is caused by implode? Or am I losing the connection somewhere between $this->conn->instance() and the function?

echo $result;

produces: Resource #10

echo $row;{/PHP]
produces: Resource #100

Can you explain this?

yep, that’s your problem there. You have extra quotes around the email address.

Have a look at example #1 in implode() but I suspect you have additional quotes (but I’m not sure) in the elements in the array $field.

I would need to see the elements in $field.

Just above

sField [COLOR=#007700]= [/COLOR][COLOR=#0000bb]implode[/COLOR][COLOR=#007700]([/COLOR][COLOR=#dd0000]', '[/COLOR][COLOR=#007700], [/COLOR][COLOR=#0000bb]$field[/COLOR][COLOR=#007700]);[/COLOR]

add

 
foreach($field as $value) {
    echo $value.'<br />';
}
 
die();

and post the output.

‘eme@example.com’
is what is in sField produces by the echo.

I changed:
$stmt = “SELECT email FROM users WHERE email = ‘$sField’”;
to:
$stmt = “SELECT email FROM users WHERE email = $sField”;

yep, so the extra quotes are in the element in $field as I suspected.

Imho they don’t need to be there but I assume you have some reason to include them as part of the actual email address.

that should fix the problem.

But if each element in $field contains a complete email address I don’t understand why you need to use implode() at all.

I’m trying to take emails entered and check them against a database

If all you want to do is check if an email address exists in the database, some simple logic based on that in post #3 should suffice.

Not sure how pretty it is, nor was it how I initially drew it up, but it works correctly now :).

		function isValidEmail($field) {
			$sField = strtolower($field);
			//echo "$sField"; die();
			$stmt = 'SELECT email FROM users WHERE email = "' . $sField . '"';
			$result = mysql_query($stmt);
			//echo "$result"; die();
			if(!$result) die("<p>Could not access database.</p>");
			$rows = mysql_num_rows($result);
			//echo "$rows"; die();
			if($rows > 0) {
				print "<div id='duplicate' class='error'>Duplicate Entry: Email already exists</div>";
				return(0);
			} else {
				return(1);
			}
				
			print "Should Not Reach This Point!<BR><BR>";
			return(false);
		}

		function insertEmail($tableName, $sValues) {
			$id = null;
			$arValueList = array();

			$validEmail = $this->isValidEmail($sValues);
			//echo "$validEmail"; die();
			
			//make sure the table name is properly escaped
			$tableName = $this->conn->quoteIdentifier($tableName);
			
			$sql = "INSERT INTO $tableName (email) VALUES ('$sValues')";
			//Only INSERT if there are no duplicates
			//If one exists, the Message is shown in function isValidEmail
			if($validEmail) {
				$result = $this->conn->query($sql);
			}

			if(DB::isError($result)) {
				throw new Exception($result->getMessage(), $result->getCode());
			}
			
			//Return the ID if there was one, or return the number of affected rows
			return $id ? $id : $this->conn->affectedRows();
		}

Thanks alot for sticking with me :slight_smile:

looks ok to me :slight_smile:

a minor change I could suggest, but it’s not a big deal, is
you could change

 
 [COLOR=#0000bb]$rows [/COLOR][COLOR=#007700]= [/COLOR][COLOR=#0000bb]mysql_num_rows[/COLOR][COLOR=#007700]([/COLOR][COLOR=#0000bb]$result[/COLOR][COLOR=#007700]); [/COLOR]
[COLOR=#ff8000]//echo "$rows"; die(); [/COLOR]
[COLOR=#007700]if([/COLOR][COLOR=#0000bb]$rows [/COLOR][COLOR=#007700]> [/COLOR][COLOR=#0000bb]0[/COLOR][COLOR=#007700]) {[/COLOR]

to

 
[COLOR=#007700]if([COLOR=#0000bb]mysql_num_rows[/COLOR][COLOR=#007700]([/COLOR][COLOR=#0000bb]$result[/COLOR][COLOR=#007700]) [/COLOR][/COLOR][COLOR=#007700]> [/COLOR][COLOR=#0000bb]0[/COLOR][COLOR=#007700]) {[/COLOR]

to eliminate 1 line of code, but it’s totally optional.

Also, I would remove

 
print "Should Not Reach This Point!<BR><BR>"; 
return(false); 

since your code can’t get there

Again, thanks a lot. Now, on to Sessions! I don’t sleep…apparently…

oh, and before I forget, I strongly recommend sanitising any inputs to sql queries using mysql_real_escape_string() before you run the query to help prevent [URL=“http://unixwiz.net/techtips/sql-injection.html”]sql injection

 
 
 [COLOR=#0000bb]$stmt [/COLOR][COLOR=#007700]= [/COLOR][COLOR=#dd0000]'SELECT email FROM users WHERE email = "' [/COLOR][COLOR=#007700]. mysql_real_escape_string([/COLOR][COLOR=#0000bb]$sField) [/COLOR][COLOR=#007700]. [/COLOR][COLOR=#dd0000]'"'[/COLOR][COLOR=#007700];[/COLOR]


Okay I’ve rewritten my DB class to be more generic. I have an insert class to accept two fields: 1. the table name(string) and 2. the variables and keys(array).

function insert($tableName, $arValues) {
                $id = null;
		$sFieldList = join(', ', array_keys($arValues));
		$arValueList = array();
			
		foreach($arValues as $value) {
			if(strtolower($value) == '#id#') {
				//we need to get the next value from thie tables sequence
				$value = $id = $this->conn->nextID($tableName . "_id");
			}
			$arValueList[] = $this->conn->quoteSmart($value);
		}
			
		$sValueList = implode(', ', $arValueList);
		$tableName = $this->conn->quoteIdentifier($tableName);
			
		$sql = "INSERT INTO " . mysql_real_escape_string($tableName) ."($sFieldList) VALUES ( " . mysql_real_escape_string($sValueList) ." )";
		$result = $this->conn->query($sql);

Taking the advice I’ve incorporated mysql_real_escape_srting. It’s causing my sql statement to fail for obvious reasons:

SQL: INSERT INTO users(email, password) VALUES ( \‘me@example.com\’, \‘foo\’ )

Why is tableName come through without the escapes and the array-to-string imploded elements to not?

Okay, so after reading some docs I have found that magic_quotes_gpc is enabled so I’ve run the $sValueList through stripslashes() first. Still the same result…as I’d expect.

You shouldn’t apply mysql_real_escape_string to all the values concatenated together with commas, but rather on each of values separately and then add the comma’s.

Also, what is your quoteSmart() function doing?

To quote the documentation: http://pear.activeventure.com/package/package.database.db.db-common.quotesmart.html

DB_common::quoteSmart() – Formats input so it can be safely used as a literal
Synopsis

mixed quoteSmart (mixed $in)

Description

Format input so it can be safely used as a literal in a query. Literals are values such as strings or numbers which get utilized in places like WHERE, SET and VALUES clauses of SQL statements.

The format returned depends on the PHP data type of input and the database type being used.
Parameter

mixed $in

the input to be quoted 

Return value

mixed - the formatted data

The format of the results depends on the input’s PHP type:

*

  input -&gt; returns
*

  NULL -&gt; the string NULL
*

  integer or float -&gt; the unquoted number
*

  boolean -&gt; output depends on the driver in use

  Most drivers return integers: 1 if true or 0 if false. Some return strings: TRUE if true or FALSE if false. Finally one returns strings: T if true or F if false. Here is a list of each DBMS, the values returned and the suggested column type:

      o

        dbase -&gt; T/F (Logical)
      o

        fbase -&gt; TRUE/FALSE (BOOLEAN)
      o

        ibase -&gt; 1/0 (SMALLINT) [1]
      o

        ifx -&gt; 1/0 (SMALLINT) [1]
      o

        msql -&gt; 1/0 (INTEGER)
      o

        mssql -&gt; 1/0 (BIT)
      o

        mysql -&gt; 1/0 (TINYINT(1))
      o

        mysqli -&gt; 1/0 (TINYINT(1))
      o

        oci8 -&gt; 1/0 (NUMBER(1))
      o

        odbc -&gt; 1/0 (SMALLINT) [1]
      o

        pgsql -&gt; TRUE/FALSE (BOOLEAN)
      o

        sqlite -&gt; 1/0 (INTEGER)
      o

        sybase -&gt; 1/0 (TINYINT(1)) 

  [1] Accommodate the lowest common denominator because not all versions of have BOOLEAN.
*

  other (including strings and numeric strings) -&gt; a string which has been escaped in a DBMS specific way (using escapeSimple()) and then surrounded by single quotes 

Note

This function can not be called statically.