I'm extremely new to PHP and my first project was developing a comment system. So now it is great but I would like to give the commenters the choice to get emails when there is a new comment. First I decide to create another row in my table called:
'email_notifications'. In the submit.php for the comment form.
To email everyone.... I wan't to use:
$emails = mysql_query("SELECT * FROM email_notifications");
while($row=mysql_fetch_assoc($emails)) {
mail($row['email'],'New Comment on...','There was a new comment o....',"From: no-reply#jdrag.x10.mx");
}
I'm not even sure this would work. But my first concern was that it was going to email people more than once.
Because, if people comment more than once with the same email, wont it just email that email more than once when it goes though the database for all the emails. Thats my big concern. Anyway I could email people when there is a new comment without email people more than once each time there is a new comment.
Summary(what I want to accomplish):
1. Someone submits a comment.
2. everyone above gets an email
I hope I made myself clear. If not, I apologize. Thanks in advance. Remember, im still a beginner :)
Assuming your table contains everyones email address this should work:
$emails = mysql_query("SELECT * FROM email_notifications");
while($row=mysql_fetch_assoc($emails)) {
$subject = "There was a new comment";
$message = "no-reply#jdrag.x10.mx";
mail($row['email'],$subject,$message);
}
However I would try to use Prepared Statments or Stored Proceedures for your database calls.
Heres the same example using Prepared Statements:
$mysqli=new mysqli("server", "username", "password", "database");
$stmt = $mysqli->prepare("SELECT email FROM email_notifications");
$stmt->execute();
$stmt->bind_result($email);
while($stmt->fetch()){
$subject = "There was a new comment";
$message = "no-reply#jdrag.x10.mx";
mail($email,$subject,$message);
}
I would not recomment putting the mail() function inside any loop, there's a possibility to create too many mails in a short timestan, which will probably result in your domain considered a spammer.
In your code where the new comment is created you can track all distinct identifiers for all comments before the created one:
$query = mysql_query("SELECT DISTINCT `userId` FROM `comments` WHERE `object` = {$yourObject} AND `userId` != {$currentUser}");
This will select all user identifiers except for the user who posted the latest comment. Then you have to create the notifications in your appropriate table, watching out for duplicates so as to prevent multiple identical notifications.
Last part is creating a mail queue script on your server that will slowly send the notifications. There are many ways to to that, i prefer a CLI script which is launched via crontab (if not launched already). There, there's just an infinite-looping cycle with a sleep of like 5 seconds to a minute (if no notifications are found) and a simple mail() sending. Here's an improvised example based on an existing CLI script:
#!/usr/bin/php -q
<?php
// acquire a process id file or prevent launching a second instance.
$filename = "/path/to/mailer.pid";
$file = fopen($filename, 'w+');
if (!flock($file, LOCK_EX + LOCK_NB)) exit(0);
// force an uninterruptable script
ignore_user_abort(true);
set_time_limit(0);
// mysql connection routines somewhere here
while (true) {
$query = mysql_query("SELECT * FROM `mail_notifications` ORDER BY `timestamp` ASC LIMIT 0, 1");
if (!mysql_num_rows($query)) {
// Oh, no notifications pending. Take a little break
sleep(60);
continue;
}
$data = mysql_fetch_assoc($query);
// your mailing magic here
sleep(5);
}
?>
Serious people write POSIX-compatible scripts which can be launched as daemons, I didn't have the urge to try that, though. The infinite loop is a dangerous thing, but that works for me at least.
Related
$i = 1;
foreach ($recipients as $email => $name) {
$mail->ClearAddresses();
$mail->AddBCC($email, $name);
if (!$mail->send()) {
$send = 0;
} else {
$send = 1;
}
$query = "INSERT INTO `newsletter_send`(`email`, `id_newsletter`, `date`, `send`) VALUES ('$email',$id_newsletter, NOW(),$send) ";
$stmt = $link->prepare($query) or die('error');
$stmt->execute();
$mail->clearAllRecipients();
if (($i % 100) == 0) {
sleep(60);
}
$i++;
}
What is the best way to send a large emails without sleep() and without to wait the page to finish loading? In addition to the cron job you have other ideas ?
EDIT: I have 680 users who will receive the email, but after a while I get 500 Internal Server Error.. why? It maybe time_limit?
Message queues.
beanstalkd is a good solution.
You can then use a SDK like pheanstalk to handle the queue and its jobs.
EDIT: If you have restricted access to your server (for example, if you are using a shared hosting) message queues as a service are also an option.
IronMQ
CloudAMQP
AWS (Amazon Web Services) SQS
A good way to send a large amount of emails at a fast pace is to have a lot of worker scripts doing the job instead of 1 php page (GiamPy gave a good example for one of the ways that can be done and I won't mention it since I don't want to be redundant).
One simple (though somewhat hacky) option is: for you to make 20 php scripts in a file. You could name them mailer1.php, mailer1.php, ..., mailer20.php. Then, you could create a folder called mail and put two files inside:
mail/config.txt
and
mail/email.txt
Inside mail/config.txt, you would include the following lines of text:
T
15
where the first line has a T for TRUE meaning you want the mailers to send the mail out as fast as they can in intervals of 15 seconds each. You can obviously change the interval time as well to whatever you like.
And in mail/email.txt you would have the complete email you want to send
After having done all that you make the mailer files. You can make 1 first, write the code, and then copy paste it 19 times to have 20 scripts in total. The code inside could look something like this:
<?php
$pathconfig = "mail/config.txt";
$pathemail = "mail/email.txt";
$email = file_get_contents($pathemail);//now you have the email saved
$filehandleconfig = fopen($pathconfig, "r");
$bool = trim(fgets($pathconfig));
$sleeptime = (integer) trim(fgets($pathconfig));
fclose($filehandleconfig);
while ($bool === 'T')
{
//... code that sends the email
//recheck if 'T' is still 'T':
$filehandleconfig = fopen($pathconfig, "r");
$bool = trim(fgets($pathconfig));
fclose($filehandleconfig);
sleep($sleeptime);
}
?>
So what the previous code would basically do is extract the email that needs to be sent at the beginning, and also extract the time it will sleep after sending an email, and if it should continue to send emails.
What that means is that the mail/config.txt file is your controlpanel, and if you change 'T' to be anything else that 'T' (like 'F' for instance), then all the scripts will terminate.
The downside to this option is that it's a bit hacky, though the upside is that it can be developed in a matter of minutes.
This is more of a logic question than language question, though the approach might vary depending on the language. In this instance I'm using Actionscript and PHP.
I have a flash graphic that is getting data stored in a mysql database served from a PHP script. This part is working fine. It cycles through database entries every time it is fired.
The graphic is not on a website, but is being used at 5 locations, set to load and run at regular intervals (all 5 locations fire at the same time, or at least within <500ms of each-other). This is real-time info, so time is of the essence, currently the script loads and parses at all 5 locations between 30ms-300ms (depending on the distance from the server)
I was originally having a pagination problem, where each of the 5 locations would pull a different database entry since i was moving to the next entry every time the script runs. I solved this by setting the script to only move to the next entry after a certain amount of time passed, solving the problem.
However, I also need the script to send an email every time it displays a new entry, I only want it to send one email. I've attempted to solve this by adding a "has been emailed" boolean to the database. But, since all the scripts run at the same time, this rarely works (it does sometimes). Most of the time I get 5 emails sent. The timeliness of sending this email doesn't have to be as fast as the graphic gets info from the script, 5-10 second delay is fine.
I've been trying to come up with a solution for this. Currently I'm thinking of spawning a python script through PHP, that has a random delay (between 2 and 5 seconds) hopefully alleviating the problem. However, I'm not quite sure how to run exec() command from php without the script waiting for the command to finish. Or, is there a better way to accomplish this?
UPDATE: here is my current logic (relevant code only):
//get the top "unread" information from the database
$query="SELECT * FROM database WHERE Read = '0' ORDER BY Entry ASC LIMIT 1";
//DATA
$emailed = $row["emailed"];
$Entry = $row["databaseEntryID"];
if($emailed == 0)
{
**CODE TO SEND EMAIL**
$EmailSent="UPDATE database SET emailed = '1' WHERE databaseEntryID = '$Entry'";
$mysqli->query($EmailSent);
}
Thanks!
You need to use some kind of locking. E.g. database locking
function send_email_sync($message)
{
sql_query("UPDATE email_table SET email_sent=1 WHERE email_sent=0");
$result = FALSE;
if(number_of_affacted_rows() == 1) {
send_email_now($message);
$result = TRUE;
}
return $result;
}
The functions sql_query and number_of_affected_rows need to be adapted to your particular database.
Old answer:
Use file-based locking: (only works if the script only runs on a single server)
function send_email_sync($message)
{
$fd = fopen(__FILE__, "r");
if(!$fd) {
die("something bad happened in ".__FILE__.":".__LINE__);
}
$result = FALSE;
if(flock($fd, LOCK_EX | LOCK_NB)) {
if(!email_has_already_been_sent()) {
actually_send_email($message);
mark_email_as_sent();
$result = TRUE; //email has been sent
}
flock($fd, LOCK_UN);
}
fclose($fd);
return $result;
}
You will need to lock the row in your database by using a transaction.
psuedo code:
Start transaction
select row .. for update
update row
commit
if (mysqli_affected_rows ( $connection )) >1
send_email();
I am developing a desktop software where it charge user per execution the main action. For example say it will charge user 0.1$ for per PDF print.
and my software provide multithreading. .
so, if it run single thread it works fine :)
but the problem is if user run multiple thread at one (say 10/20 threads)
it (php) also continues user to allow the server/execution even balance get below zero..
though my php script check whether balance is positive ..
but after user run multiple threads balance become like -5.95$ or -25.75$ etc
and that is a big security/financial issue..
here is the code I am using:
<?php
$strSQL = "Select * from users where Email = '$strUser'";
$return = mysql_query($strSQL, $strDBConn);
$strDBData = mysql_fetch_array($return, MYSQL_ASSOC);
//checking balance
$strBalance = $strDBData['Balance'];
if($strBalance < 0)
{
// if balance 0 then exit so, my software/thread will not process further
mysql_close($strDBConn);
exit('Balance Exceed');
}
//rest of the codes that realted to service executaion
// code that substract the balnce
$dblCost = 0.25;
$strSQL = "Update users set Balance = Balance - '$dblCost' where Email = '$strUser'";
$return = mysql_query($strSQL, $strDBConn);
//rest finising codes
?>
any help/suggestion would be highly appreciated..
thanks in advance.
best regards
I think, this is a quite similar question:
What is equivalent of the C# lock statement in PHP?
First, try to switch away from the old "mysql" to somethin new, maybe some PDO like DB access ;).
Then, for getting around with multi-thread in php, it can be a good idea, to write a file for every userid (!) and lock this file, when there's a request. When file is locked in another thread, wait for x seconds for the file to be unlocked by the locker-thread. If it is not unlocked within time, something went wrong. When in locked-thread all went good, unlock the file after every operation needed.
Theoraticaly you will be good with then till there's a multi-thread soloution in PHP ;)
Currently I have a file called "hits.php" and on any page I want to track page hits I just use <?php include("hits.php"); ?>
How can I track unique visitors only though? My hits are false since it can be refreshed by the same person and hits go up.
Here's my source:
<?php
$hits = file_get_contents("./client/hits.txt");
$hits = $hits + 1;
$handle = fopen("./client/hits.txt", "w");
fwrite($handle, $hits);
fclose($handle);
print $hits;
?>
I don't really know how I could do cookie checking... is there a way to check IP's? Or what can I do?
Thanks StackO.
The simplest method would be cookie checking.
A better way would be to create an SQL database and assign the IP address as the primary key. Then whenever a user visits, you would insert that IP into the database.
Create a function included on all pages that checks for $_SESSION['logged'] which you can assign whatever 'flag' you want.
If $_SESSION['logged'] returns 'false' then insert their IP address into the MySQL database.
Set $_SESSION['logged'] to 'true' so you don't waste resources logging the IP multiple times.
Note: When creating the MySQL table, assign the IP address' field as the key.
<?php
session_start();
if (!$_SESSION['status']) {
$connection = mysql_connect("localhost", "user", "password");
mysql_select_db("ip_log", $connection);
$ip = $_SERVER['REMOTE_ADDR'];
mysql_query("INSERT INTO `database`.`table` (IP) VALUES ('$ip')");
mysql_close($connection);
$_SESSION['status'] = true;
}
?>
There isn't a perfect solution, but the first two methods (IP address and/or cookies) are the most reliable, and a combination might be even better.
Rather than reinventing the wheel I used an off the shelf solution. For commercial reasons I avoided Google Analytics (I don't want Google to know my web stats - their best interests are not mine). They're probably fine for non-commercial websites, or if you don't use Google for advertising. There are also dozens of alternatives. Eg I use Getclicky.com
At a basic level, you can get the client's IP address by using the PHP $_SERVER['REMOTE_ADDR'] property
Consider setting a cookie or using a session, though this can be defeated by deletion of a cookie or cookie rejection. See the PHP setcookie docs for more info.
There are other methods for browser fingerprinting - check out all the different data you could conceivably use at https://coveryourtracks.eff.org/
How about google analytics if you cant. you could do a database or create another file with the IPs in it, but it could get complicated with a flat file like that.
I found the solution of very poor quality and just a quick and dirty way of doing it.
I too was developing something similar and formulated a quick method which works without redundancy.
I needed a counter for every time someone accessed another user's profile.
Pseudo:
Create a table with viewer's name and viewee's name (daily_views table).
Check to see if exists the viewer's name with the viewee's name (on the same row).
If they do not exist, update user counter +1 (in users table).
Else do nothing.
Reset entire table values null every 24/12 hours via cron job.
This will deny the same person accessing the same user profile to add 1 to the
counter on refresh for the whole day (or 12 hours) whereas the above solution
by Glenn Nelson would indeed add 1 to the counter, but deny adding to every
user's counter at the same time.
Not only this, but if you were to logoff and log back in to the website, then
it would simply re-add to the counter in which some cases trolls and haxorz
wannabe's will exploit this (as the session is destroyed and started again).
Here are my sample tables:
users
{
user_id INT(8) auto increment, user_name varchar(32), user_counter INT(12)
};
daily_views
{
view_id INT(8) auto increment, viewer_name VARCHAR(32), viewee_name VARCHAR(32)
};
Here is sample code I've written:
<?php
session_start();
$uname = $_SESSION['username'];
$vieweepage = $_GET['name']; //gets the name of the persons page/profile via previous page/form
$connect = mysql_connect("localhost","user","password") or die("Couldn't connect; check your mysql_connect() settings");
$database = mysql_select_db("database") or die("Could not locate database!");
$query = mysql_query("SELECT user_counter from users");
$query = mysql_fetch_row($query);
$counter = $query[0];
$viewcheck = mysql_query("SELECT viewer_name from daily_views WHERE viewee_name='$vieweepage'");
$viewrow = mysql_num_rows($viewcheck);
$newcounter = $counter + 1;
if($viewrow == 0)
{
$update = mysql_query("UPDATE users SET user_counter='$newcounter' WHERE user_name='$vieweepage'");
$insert = mysql_query("INSERT into daily_views (viewer_name, viewee_name) VALUES ('$uname', '$vieweepage')");
}
?>
currently i am using remote address and session ID for visitor.i think its valid visitor because a single user can visit no of times in a days and counter not depends on refresh its only depends on new session.
You could save a timestamp to localStoage in javascript. LocalStoage isn't removed by the browser, so you should be save to check against that. I know that it isn't serverside checking, but it may be helpful anyway.
I would like to automate the process of sending this email. I mean when a condition is met then the email should automatically be sent to the given email addresses which is already given as hard-coded values. So for an example in my case a Technician has to complete 40jobs a day and if he finished 35 at the end of the day an email should be sent to the supervisor(provided his email id is already given) with Subject name and the body should be like ” The Technician 1234 has completed only 35 jobs for the day instead of 40. I was wondering how can implement this as Im very new to the field of PHP. Please anyone help me out. If possible please provide me with an example.
Thanks
Dilip
You could run a cron that uses the php mail function to send a report about each programmer at a set time each day. The cron script would look something like:
<?php
$to = 'admin#mydomain.com';//the set up email to mail too
$result = mysql_query('select programmer_id, job_count, job_requirement from table'); //query the database
while($job = mysql_fetch_object($result)){
mail($to,'Job counts for ' . $job->programmer_id,"Completed $job->job_count out of $job->job_requirement jobs","FROM: jobcounter#mydomain.com");
}
Have a script check the conditions and send emails, and run it periodically: http://www.google.com/search?q=cronjob
I'm assuming here that your problem is the triggering of the event, rather than the sending of the email itself.
In the example you described, you would need to have a script run at a certain time of day that would check for any condition that would require an email sending, and then send the email. Under any unix-like system, cron is the ideal solution to this. If you are on some kind of basic shared hosting you may not be able to set this up. In that case you would need to set up a task to run on a machine that you do have control over that would call a URL that would run the PHP script. This could be a cronjob, or a Scheduled Task under Windows.
If your example was switched around so that, say, an email was to be sent as soon as a technician completed 40 jobs then you would be able to send the email as part of the script that handled the form submission from the technician whenever he completed a task.
Setup a Cron Job that runs at the "end of the day":
23 55 * * * /path/to/php /path/to/script.php
Have it run a PHP script that can query your Job Store for whatever condition you want to check. For example with Job Store being a database.
$db = new PDO(/* config */);
$result = $pdo->query(
'SELECT count(id) as "tasks_done"
FROM tasks WHERE engineer = "Tom"
AND finished_at = now()');
$result = $result->fetchAll();
if($result[0]['tasks_done'] < 40) {
mail( ... )
}
If the condition is met, send the mail. The above needs refining of course. Dont expect being able to copy/paste it.
Also see:
What is the best method for scheduled tasks in PHP
http://greenservr.com/projects/crontab/crontab.phps
you should use mail() function:
$technicianId = 1234;
$jobsNeeded = 40;
$jobsDone = getJobsDone($technicianId);
if ($jobsDone <= $jobsNeeded) {
mail('supervisor#yourcompany.com', 'Technician '.$technicianId.' slacking', 'The Technician '.$technicianId.' has completed only '.$jobsDone.' jobs for the day instead of '.$jobsNeeded);
}
Could have a cronjob that runs at the end of each day which checks what each technician has done and emails it to the manager.