How to email all users with user-specific information. - php

I've built a contest system for a website, how it works is a user logs in, submits a ballot based on a real life event (sale of a particular object), and then at the end of the month, a random ballot is chosen and the owner of that ballot is the winner.
I've been asked to create a script which will email all users in the database the current amount of ballots they have in the system.
My current login/registration system is a heavily edited version of HTML-Form-Guies Simple PHP Registration System.
I know the pseudo code for what I want to do.
Step by step, the method needed goes like this.
Call on EmailUsersTotalEntries, populates an array with all the users in the database, pick the first entry in the array, user 1, find the sum of the all the rows in the itemsold column with the userid 1. then send user one an email with the results of the select sum(itemsold) from ballots where userid = 1; to user 1. Then the loop goes to user 2 and does the same thing, until it has sent an email to every user in the database.
Here are a few of the methods that I have either written or that are from the login system that will be used to accomplish this. My only problem is I dont know how to make a loop so that it will start from user 1 and then keep going all the way to user 2, and I dont know how to query the database for the user_id of whatever user the database/loop is currently on.
Methods are as follows:
This is the main method, it will call sub methods to collect the users and then send the actual email. I'm not sure if TotalEntries should be an array or not
function EmailTotalEntries()
{
if(empty($_POST['email']))
{
$this->HandleError("Email is empty!");
return false;
}
$user_rec = array();
if(false === $this->GetUsers($user_rec))
{
return false;
}
**/* $TotalEntries = array(); */**
if(false === $this->GetTotalEntriesForEmail($user_rec, $TotalEntries)
{
return false;
}
//At this point, I have an array, user_rec, populated with all the data from my users table, and an array $TotalEntries that will have nothing since its trying to pull from user_rec, which usually is one user but right now is all of the users.
//This is where I know I should have already started the loop, so chosen the first element in user_rec, and applied the GetTotalEntriesForEmail method, then the SendUserEmail method, then gone to the top of the loop and gone to the second user_rec element and repeat.
if(false === $this->SendUsersEmail($user_rec, $TotalEntries))
{
return false;
}
return true;
}
This is the method that collects the users
function GetUsers(&$user_rec)
{
if(!$this->DBLogin())
{
$this->HandleError("Database login failed!");
return false;
}
$result = mysql_query("Select * from $this->tablename",$this->connection);
$user_rec = mysql_fetch_assoc($result);
return true;
}
Here is the method I wrote to get the TotalEntries for a user that is logged in (checking his control panel to see how many entries he has)
function GetTotalEntries()
{
if(!$this->CheckLogin())
{
$this->HandleError("Not logged in!");
return false;
}
$user_rec = array();
if(!$this->GetUserFromEmail($this->UserEmail(),$user_rec))
{
return false;
}
$qry = "SELECT SUM(itemsold) AS TotalEntries FROM entries WHERE user_id = '".$user_rec['id_user']."'";
$result = mysql_query($qry,$this->connection);
while($row = mysql_fetch_array($result))
{
echo $row['TotalEntries'];
}
}
And here is how I believe it needs to be adapted to work in the email.
function GetTotalEntriesForEmail($user_rec, &$TotalEntries)
{
if(!$this->DBLogin())
{
$this->HandleError("Database login failed!");
return false;
}
$qry = "SELECT SUM(itemsold) FROM entries WHERE user_id = '".$user_rec['id_user']."'"; //$user_rec['id_user'] should the be id of the user the loop is currently on.
$result = mysql_query($qry,$this->connection);
$TotalEntries = mysql_fetch_assoc($result);
return true;
}
Heres the actual email
function SendUsers($user_rec, $TotalBallots)
{
$email = $user_rec['email']; //should be the for the user the loop is currently on.
$mailer = new PHPMailer();
$mailer->CharSet = 'utf-8';
$mailer->AddAddress($email,$user_rec['name']); //user the loop is currently on.
$mailer->Subject = "Total Ballots to Date";
$mailer->From = $this->GetFromAddress();
$mailer->Body ="Hello ".$user_rec['name']."\r\n\r\n". //Same thing
"To date you have: "/* .$TotalBallots. */" ballots.\r\n" //Same thing
if(!$mailer->Send())
{
return false;
}
return true;
}
I'm not very good at PHP, and this whole thing is a learning experience for me, so help is greatly appreciated.
If I havent been clear, maybe giving an example in another language would be clearer, so heres what I want to do, but in java
for(x = 0; x <= user_rec.length; x++)
{
int ballots = getTotalEntriesForUser(x);
sendEmailToUser(ballots)
}
If I havent been clear enough, please let me know and I will try to clarify as best as possible.
How can I combine the above code with a loop that will send all users an email, one by one, each email unique to the user it is sent to?

Are your functions part of a class? You wouldn't necessarily need them to do this. Here's my recommendation, which you can turn into functions, or a class, if you want. Also, you may want to consider looking into, and using MySQLi, and taking advantage of the classes it uses. Again, all just my recommendations.
Without knowing your table structure, I'm just taking a guess at this.
$sql = mysql_query("SELECT u.*,
u.user_id AS user,
COALESCE(SUM(e.itemssold), 0) AS total_items
FROM users u
LEFT JOIN entries e ON e.user_id = u.user_id
GROUP BY u.user_id");
while($row = mysql_fetch_array($sql))
{
$user = $row['user'];
$email = $row['user_email'];
$items = $row['total_items'];
yourEmailFunction($email, $items);
}
This pulls information from your users table, and your entries table based on matching User ID's. It sets the User ID from the user table as user so you don't have to try and distinguish between the two later. To learn about the COALESCE function, read here. The while() function will loop through every user it pulls from that SQL statement.
This hasn't been tested in any way, but that's basically what you need. Just pass the User's email, and the total Items, and write your email function to send that info to that email address.
However, if you know your functions work properly, and want to use a for loop, such as the one you provided in Java, here's how you'd write it in PHP.
for($x = 0; $x <= count($user_rec); $x++)
{
$ballots = getTotalEntriesForUser($x);
sendEmailToUser($ballots);
}

Related

PHP Loop through dynamic array

I have a webapplication with a php backend.
At a certain interval checks need to be run on all users. Running these checks takes some time, more importantly: they should not be executed on non-existing users. The users are received from a database and can change mid-way through running the checks. My current solution is:
<?php
require_once 'databaseUtils.php';
$users = getUsersFromDatabase();
//Sample:
while(true) {
foreach(GetUsers() as &$user) {
//I'd rather not:
if(checkIfUserIsInDatabase($user) {
var_dump($user);
sleep(1); //Checking takes time...
}
}
echo "Going again in 5s!";
sleep(5); //Interval
}
function GetUsers() {
return $users;
}
//Called from outside
function removeUser($user) {
global $users;
removeUserFromDatabase($user);
if(($key = array_search($user, $users)) !== false) {
unset($users[$key]);
}
}
?>
But I'm still looping needlessly through the user that isn't realy in the GetUsers() anymore. Is there a way to loop through all values of an array in which elements can get deleted from outside?
If you want to check each user if he is still in the database, you will need to query his information from database, so it will take more efforts than checking each item in array (if it is not more resource spending).
But I will suggest to get users in smaller portions instead of all users. And check portion by portion.
Firstly, you might need to consider to rework your entire business logic.
Do not use endless loops (while(true)) and global variables (global $users).
To loop through all users you can use generator concept and retrieve existent users one by one. So you will get only the next user from the database during each iteration:
function getUsersFromDatabase($db)
{
$id = 0;
while (($user = $db->query("SELECT * FROM `users` WHERE `id` > $id LIMIT 1"))) {
yield $user;
$id = $user->id;
}
}
foreach (getUsersFromDatabase($db) as $user) {
checkUser($user);
}
I do not know your framework or DB adapter details, so the code snippet is generic and should be considered as pseudo code.

looping, comparing multiple table values

I've been having a problem & not sure the logic I need.
I'm trying to build a basic PHP & MySQL registration page though with a check to see if they've changed IP addresses, check to see if the addresses have been registered with 3 different accounts, if so, return false.
Here is my logic.
Account registers -> Database (site_registration) -> Email verified -> Database (users) -> Account logs in -> Database (account_logins) (only if new IP)
Account tries to reregister after 3 entries -> Check Database (site_registration) IP field -> Check Database (account_logins) account IP fields -> Throw all IP's for the account into an array -> Check array against site_registration -> If IPs are found on 3 accounts, throw registration error -> Database (suspicious_logs)
This is the mysql code I need, but not sure how to loop it.
//SELECT * FROM users WHERE (idnumber = '75.143.xxx.xxx') OR (idnumber = '76.94.xxx.xxx') OR (idnumber = '76.94.xxx.xxx')
$username = $_SESSION['login'];
$check_ip_site = $MySQL->consult("SELECT * FROM site_registration WHERE (username = '$username')");
$check_ip_logins = $MySQL->consult("SELECT * FROM site_logins WHERE (username = '$username')");
$check_ip_user = $MySQL->consult("SELECT * FROM users");
for($i = 0; $login_array[$i] = mysql_fetch_assoc($check_ip_logins); $i++);
array_pop($login_array);
for($i = 0; $user_array[$i] = mysql_fetch_assoc($check_ip_user); $i++);
array_pop($user_array);
foreach ($login_array["ip"] as $login_ips) {
if (in_array($login_ips, $user_array["ip"]) > 3) {
return true;
} else {
return false;
}
}
Though this syntax doesn't work, it's what I need, I want to find if ANY of the IPs are found on 3 records in the user database, if they are return false.
This is what works for me. I had to set it into a foreach for it to loop the query. Also, my for loop works like that as I had nothing to do inside of the loop. If anyone sees a better way of doing this, please let me know! Before I accept my answer, I'll see if anyone has a better solution.
//Check if username is set, if it's not use the current IP instead (add cookie handling later to keep from destroying evidence)
$ip = $_SERVER['REMOTE_ADDR'];
if ($username == NULL){
$check_ip_logins = $MySQL->consult("SELECT * FROM site_logins WHERE (ip = '$ip')");
}
else
{
$check_ip_logins = $MySQL->consult("SELECT * FROM site_logins WHERE (username = '$username')");
}
for($i = 0; $login_array[$i] = mysql_fetch_assoc($check_ip_logins); $i++);
array_pop($login_array); //Deletes the null array at the end of the for loop
foreach ($login_array as $la)
{
$lips = $la["ip"]; //Place just the IP's into it's own array
$check_ip_user = $MySQL->consult("SELECT * FROM users WHERE (idnumber = '$lips')"); //Loop through all IP's associated with account & check the users database
while($test = mysql_fetch_assoc($check_ip_user)) {
$names[] = $test["name"]; // This is used as a way to count, any thing can be used here.
}
}
if (count($names) > 3)
{
echo 'you have too many accounts you cannot create anymore';
}
else
{
echo 'you are able to create an account';
}

Checking if user already has account with PHP & ORACLE

I'm trying to apply the finishing touches on a register form. I want to check if the user already has an account, based on his e-mail address. However, the PHP won't help me out. Here is the main part of my code.
$s = oci_parse($conn, "SELECT * FROM users WHERE mail='&mail'");
oci_execute($s);
$rows = 0;
while (oci_fetch($s))
{
$rows ++ ;
}
echo $rows; -> this echos all the time 0 even if I have 10+ registered users with same email
if($rows > 0)
{
//has account
}
else{
doesn't have account, inserting into DB
}
It seems like the value of $rows is always 0, no matter what I Do. I also tried with
SELECT COUNT(*) FROM users WHERE mail='$mail'
But i was unable to pass the correct value to the $rows variable.
Everything else works fine in my php code.
Thanks in advance
The error comes from the sql statement you have written &mail insted of $mail.
And by the way to count your rows you can use the function oci_num_rows($s) with this function you dont have to use the while loop

Checking mySQL db for duplicate uid

I am trying to implement a check in my PHP code, that checks if there is a duplicate uid in the database, and if so, to assign a new uid, and check again, but I am having trouble nailing the logic, here is what I have thus far,
function check($uid){
$sql = mysql_query("SELECT * FROM table WHERE uid='$uid'");
$pre = mysql_num_rows($sql);
if($pre >= 1){
return false;
}else{
return true;
}
}
And then using that function I thought of using a while loop to continue looping through until it evaluates to true
$pre_check = check($uid);
while($pre_check == false){
//having trouble figuring out what should go here
}
So basically, once I have a usable uid, write everything to the database, else keep generating new ones and checking them till it finds one that is not already in use.
It is probably really simple, but for some reason I am having trouble with it.
Thanx in advance!
$uid = 100; // pick some other value you want to start with or have stored based on the last successful insert.
while($pre_check == false){
$pre_check = check(++$uid);
}
Of course ths is exactly what 'auto incrementing' primary keys are useful for. Are you aware of 'auto incrementing' primary keys in mysql?
EDIT
In light of your comment regarding maintaining someone else's code that uses the random function like that (ewwwww)... I would use the method I suggest above and store the last inserted id somewhere you can read it again for the next user. This will allow you to "fill-in-the-blanks" for the uids that are missing. So, if for example you have uids 1, 2, 5, 9, 40, 100... you can start with $uid = 1; Your while loop will return once you get to 3. Now you store the 3 and create the new record. Next time, you start with $uid = 3; and so on. Eventually you will have all numbers filled in.
It is also important to realize that you will need to do the inserts by either locking the tables for WRITES. You don't want to get into a race condition where two different users are given the same uid because they are both searching for an available uid at the same time.
Indeed the best is to use autoincrement ids, but if you don't have the choice, you can do a reccursive function like that:
function find_uid() {
$new_uid = rand(1000000000, 9999999999);
$sql = mysql_query("SELECT COUNT(*) AS 'nb' WHERE uid=".$new_uid.";");
$row = mysql_fetch_assoc();
$pre = $row['nb'];
return ($pre >= 1 ? find_uid() : $new_uid);
}
COUNT(*) should be more performant because the count is made by MySQL and not php.
By the way, if you need a new uid shouldn't the condition be ($pre > 0) instead of ($pre > 1) ?

Can I get feedback on this PHP function that tests if a user has signed up?

I'm just getting started on writing functions instead of writing everything inline. Is this how a reusable function is typically written?
function test_user($user) {
$conn = get_db_conn();
$res = mysql_query("SELECT * FROM users WHERE uid = $user");
$row = mysql_fetch_assoc($res);
if (count($row) == 1) {
return true;
}
else {
return false;
}
}
When someone logs in, I have their UID. I want to see if that's in the DB already. It's basic logic will be used in a
"If exists, display preferences, if !exists, display signup box" sort of flow. Obviously it's dependent on how it's used in the rest of the code, but will this work as advertised and have I fallen for any pitfalls? Thanks!
Try this:
$conn = get_db_conn(); # should reuse a connection if it exists
# Have MySQL count the rows, instead of fetching a list (also prevent injection)
$res = mysql_query(sprintf("SELECT COUNT(*) FROM users WHERE uid=%d", $user));
# if the query fails
if (!$res) return false;
# explode the result
list($count) = mysql_fetch_row($res);
return ($count === '1');
Thoughts:
You'll want better handling of a failed query, since return false means the user doesn't already exist.
Use the database to count, it'll be faster.
I'm assuming uid is an integer in the sprintf statement. This is now safe for user input.
If you have an if statement that looks like if (something) { true } else { false } you should collapse it to just return something.
HTH
That is reuseable, yes. You may want to consider moving the SQL out of the PHP code itself.
Although you weren't asking for optimization necessarily, you might want to consider querying for the user's display preferences (which I assume are stored in the DB) and if it comes back empty, display the signup box. You'll save a trip to the database and depending on your traffic, that could be huge. If you decide to keep this implementation, I would suggest only selecting one column from the database in your SELECT. As long as you don't care about the data, there's no reason to fetch every single column.
First off, you need to call
$user = mysql_real_escape_string($user);
because there's an sql injection bug in your code, see the manual. Second, you can simplify your logic by changing your query to:
SELECT COUNT(1) FROM user WHERE uid = $user;
which just lets you evaluate a single return value from $row. Last thing, once you have the basics of php down, consider looking at a php framework. They can cause you trouble and won't make you write good code, but they likely will save you a lot of work.
Indent!
Overall it looks not bad...check the comments..
function test_user($user)
{
$conn = get_db_conn(); //this should be done only once. Maybe somewhere else...?
$res = mysql_query("SELECT uid FROM users WHERE uid = $user");
$row = mysql_fetch_assoc($res);
//I can't remember...can you return count($row) and have that forced to boolean ala C? It would reduce lines of code and make it easier to read.
if (count($row) == 1) {
return true;
}
else {
return false;
}
}
Also,
if (condition) {
return true;
}
else {
return false;
}
can be rewritten as:
return condition;
which saves quite a bit of typing and reading :)

Categories