I have an array of email addresses I am sending emails to.
I would like to sort them by alternating domain names, so if I have 30 #gmail.com, 30 #yahoo.com and 30 #aol.com, the sort would result in a #gmail.com, then #yahoo.com, then #aol.com, then #gmail.com again, etc.
The sort would alternate as much as possible so that there would be as few identical domain names in a row.
Why: To prevent being considered as a source of spam, its best to "throttle" email sending, or sleep between each send so mail servers are not hit quickly many times in a short time spam. Instead, I would like to do that above to create a lag between times an email provider is hit by me, but without stopping my script and causing a delay to my end user.
I may do it like this:
$organized_emails = array();
$needle_key = 0;
$needle_search = array('gmail', 'yahoo', 'aol', 'others');
while(true) {
$current_value = array_shift($emails);
if(strpos($current_value, $needle_search[$needle_key]) !== false) {
$organized_emails[] = $current_value;
$needle_key++;
if($needle_key > 3) {
$needle_key = 0;
}
} else {
array_push($emails, $current_value);
}
if(empty($emails)) {
break;
}
}
PHP Fiddle sample
Related
I could not find an example of what I'm trying to accomplish. I guess I don't know the proper search words to use. I have a working script but I want to make it more flexible by adding a main, admin editable config file.
I have the following function:
function ip_is_mobile($ip) {
$pri_addrs = array(
'66.87.0.0-66.87.255.255', // Sprint mobile
'174.192.0.0-174.255.255.255' // Verizon mobile
);
$long_ip = ip2long($ip);
if($long_ip != -1) {
foreach($pri_addrs AS $pri_addr) {
list($start, $end) = explode('-', $pri_addr);
// IF IS a mobile IP
if($long_ip >= ip2long($start) && $long_ip <= ip2long($end))
return true;
}
}
return false;
}
I would like to replace the hard-coded IP address ranges, in the function, with variables or definitions which will be set in the main config file so that the config file has something similar to the following:
// Mobile IP address ranges. Add as many as needed.
$MobileIPs['0']="66.87.0.0-66.87.255.255";
$MobileIPs['1']="174.192.0.0-174.255.255.255";
$MobileIPs['2']="85.110.50.0/24";
My goal is to give the admin an easy to read and understand way of adding as many IP address ranges as necessary (probably 20 max). I'm not opposed to totally rewriting the function if there is a better, more efficient way. In addition to IP ranges, it would be advantageous if CIDR's could also be specified; as indicated in the last code line above.
What edits do I need to make to the function and what would the corresponding lines in the main config file be so that the user can add any number of ranges or CIDR's?
You can store configuration of ip ranges in separate configuration file and use require_once in your main code
ip_ranges.conf.php (configuration file)
<?php
$pri_addrs = array(
'66.87.0.0-66.87.255.255', // Sprint mobile
'174.192.0.0-174.255.255.255' // Verizon mobile
);
index.php (main code file)
function ip_is_mobile($ip) {
require_once(ip_ranges.conf.php); // include config file
$long_ip = ip2long($ip);
if($long_ip != -1) {
foreach($pri_addrs AS $pri_addr) {
list($start, $end) = explode('-', $pri_addr);
// IF IS a mobile IP
if($long_ip >= ip2long($start) && $long_ip <= ip2long($end))
return true;
}
}
return false;
}
I'm trying to write a PHP program that uses the IMAP PHP extension to run through a very large inbox (greater than 70,000 emails), and move all emails that fall within a specified date range into a folder. I keep running into problems trying to get the mail that falls within the desired range.
First I tried using the SINCE and BEFORE criteria in imap_search() to place all of the desired ID numbers into an array, and then created a string listing these ID numbers to feed into imap_mail_move(). But the problem I keep running into is that imap_search() never actually returns all of the emails within the specified range. For instance, I ran the following code:
echo "Beginning search. \n";
$mail = imap_search($this->mbox,
'SINCE "$startDate" BEFORE "$endDate"');
echo "Search complete. \n";
if (!$mail){echo "The inbox is empty. \n"; die();}
$count = 0;
foreach($mail as $i)
{
$count = $count + 1;
}
echo "Found $count emails. \n";
while specifying a date range that I know to hold 600ish emails, and imap_search() only listed 259 of them. When I expanded the range to encompass the entire inbox of about 75,000 emails, imap_search() only returned 25,112 of them.
When this didn't work, I tried a different, slower way to get all emails within the desired date range. I used imap_search() to collect ALL emails in the inbox, then I iterated through each email, compared the date contained in the emails header to the dates definining my interval, and added the ID to a string if it was in range. I ran the following code:
// Get all mail in the INBOX
$mail = imap_search($this->mbox, "ALL");
if (!$mail){echo "The inbox is empty. \n"; die();}
// Create a string list of all message IDs
//that fall in the specified range
$processed = 0;
$IDsequence = '';
foreach($mail as $messageID)
{
$date = imap_headerinfo($this->mbox, $messageID)->date;
try {$dateObject = new DateTime($date);}
catch (Exception $e) {echo $e->getMessage(); echo "\n";}
$dateObject->setTimezone($TimeZone);
if ($dateObject <= $IntervalEndDate && $dateObject >=
$IntervalStartDate)
{
$IDsequence = $IDsequence . "," . strval($messageID);
}
$processed = $processed + 1;
if ($processed % 100 == 0)
{ echo "Processed $processed emails. \n"; }
}
// If we got at least one message, cut off the first comma.
if (strlen($IDsequence) > 1){
$IDsequence = substr($IDsequence, 1, strlen($IDsequence)-1);
}
else
{
echo "No e-mail to move. \n";
die();
}
echo "Beginning copy. \n";
imap_mail_copy($this->mbox, $IDsequence, $TargetFolder);
echo "Copy complete. \n";
//imap_expunge($this->mbox);
imap_close($this->mbox);
After iterating through just over 25,000 emails, it began to print the error message 'object does not exist' referencing the line of code where I collect the date from the email header. I notice here that the breakdown still occurs somewhere around 25,000 emails, so perhaps both methods break for the same reason. I suspect that I am doing something wrong with the message IDs (for instance, I am assuming that the message IDs for the 75,000 emails are '1' through '75,000').
What is going wrong? Is there a way to collect a large number of emails that fall within a date range and move them?
Perhaps there are only 25,112 messages in the mailbox, and the other ~50,000 are in another mailbox. I know GMail, for example, may report a rough total for the Inbox, but a good percentage of those messages are actually archived.
You might want to do a sanity check at this point. :)
I am getting a 2 dimensional array value as a result after a for loop.The value is $chunk[$i][$j].And when I passed that value into setTo function,
the error showing as
Warning: preg_match() expects parameter 2 to be string, array given in H:\xampp
\htdocs\sngmarket\vendor\swiftmailer\swiftmailer\lib\classes\Swift\Mime\Headers
\MailboxHeader.php line 350.
How do I solve this?.Here my code
$query = $em->createQuery("SELECT DISTINCT u.emailaddress FROM AcmeRegistrationBundle:userlist u");
$grp_emails[] = $query->getResult();
$chunk = array_chunk($grp_emails, 10);
$get_chunk_count = count($chunk);
for($i=0;$i<$get_chunk_count;$i++)
{
$count_inside_count = count($chunk[$i]);
for($j=0;$j<=$count_inside_count;$j++)
{
$mails=$chunk[$i][$j];
$message = \Swift_Message::newInstance()
->setSubject('Hello Email')
->setFrom('marketplace#socialnetgate.com')
->setTo($mails)
->setReturnPath('gowtham#ephronsystems.com')
->setBody('Hello World');
$this->get('mailer')->send($message);
return array();
}
}
I think you are overthinking this.
Have you looked at the documentation on how to send batch emails WITHOUT recipients being aware of each other? In your snippet each email contains up to 10 recipients, which may be better then sending all recipients, but still is pretty bad.
Have a look at Sending emails in batch and also at the plugins to make sure you don't reach the limit of emails you are allowed to send in a certain time frame.
We have a web page want to limit uo to 100 people can access concurrently, so we use a memcached to implement a global counter, e.g.
We are using http://www.php.net/manual/en/class.memcache.php so there is not cas, current code is something like
$count = $memcache_obj->get('count');
if ($count < 100) {
$memcache_obj->set('count', $count+1);
echo "Welcome";
} else {
echo "No luck";
}
As you can see there is race condition in the above code and but if we are not going to replace memcached extension which support cas, it is able to support it using PHP code only?
As answer to "emcconville". This is non-blocking even without CAS.
If your concerned about race conditions, and the count value is completely arbitrary, you can use Memcache::increment directly before any business logic.
The increment method will return the current value after the incrementation takes place; of which, you can compare results. Increment will also return false if the key has yet to be set; allowing for your application to deal with it as needed.
$current = $memcache_obj->increment('count');
if($current === false) {
// NOT_FOUND, so let's create it
// Will return false if has just been created by someone else.
$memcache_obj->add('count',0); // <-- no risk of race-condition
// At this point 'count' key is created by us or someone else (other server/process).
// "increment" will update 0 or whatever it is at the moment by 1.
$current = $memcache_obj->increment('count')
echo "You are the $current!";
}
if ($current < 100) {
echo "Hazah! You are under the limit. Congrats!";
} else {
echo "Ah Snap! No Luck - you reached the limit.";
// If your worried about the value growing _too_ big, just drop the value down.
// $memcache_obj->decrement('count');
}
If your concerned about race conditions, and the count value is completely arbitrary, you can use Memcache::increment directly before any business logic.
The increment method will return the current value after the incrementation takes place; of which, you can compare results. Increment will also return false if the key has yet to be set; allowing for your application to deal with it as needed.
$current = $memcache_obj->increment('count');
if($current === false) {
// NOT_FOUND, so let's create it
$memcache_obj->set('count',1); // <-- still risk of race-condition
echo "Your the first!";
} else if ($current < 100) {
echo "Hazah! Your under the limit.";
} else {
echo "Ah Snap! No Luck";
// If your worried about the value growing _too_ big, just drop the value down.
// $memcache_obj->decrement('count');
}
function memcache_atomic_increment($counter_name, $delta = 1) {
$mc = new Memcache;
$mc->connect('localhost', 11211) or die("Could not connect");
while (($mc->increment($counter_name, $delta) === false) &&
($mc->add($counter_name, ($delta<0)?0:$delta, 0, 0) === false)) {
// loop until one of them succeeds
}
return intval($mc->get($counter_name));
}
The comments in Memcache::add include an example locking function, have you tried it out?
Ok, here's what I'm looking for: from a list of links, I'm stripping everything but the domains. The result is a mixed list of domains and domain-names which represent subdomains.
stackoverflow.com
security.stackexchange.com
whoknows.test.co.uk
you.get.it.by.now.dont.you.com
What I want to do is to trim all list entries down to their VALID (=only existing) root domains like this:
stackoverflow.com
security.stackexchange.com
test.co.uk
-fail-
Currently I explode each line into an array and work my list from back to front, using curl to check each potential root domain for it's existance... as soon as curl throws back a HTTP code >= 200 and < 400, I regard the root domain to be found. When the end of each potential domain lookup is done and no valid domain has been found at all, the domain is considered to be non-existant.
input: stackoverflow.com
test: stackoverflow.com - succeeds and is the root domain
result: stackoverflow.com - valid root domain
input: whoknows.test.co.uk
test: co.uk - fails
test: test.co.uk - succeeds (theoretically) and is the root domain
result: test.co.uk - valid root domain
input: you.get.it.by.now.dont.you.com
test: you.com - fails
test: dont.you.com - fails
test: now.dont.you.com - fails
test: by.now.dont.you.com - fails
test: it.by.now.dont.you.com - fails
test: get.it.by.now.dont.you.com - fails
test: you.get.it.by.now.dont.you.com - fails
result: you.get.it.by.now.dont.you.com - invalid domain
Is there any alternative way to do this? I would like to stop heating up my webserver's CPU with 2 to X (=near to unlimited) curl look-ups for every domain on my 100.000+ list. Also, all these lookups take a bunch of time. Maybe - so I hope - there is a quicker solution to do this.
The catch? It has to work with PHP.
There are a bunch of shortcuts to acheive what you need.
For example, you already know that .co.uk and .com are TLDs, so checking these you can obviously skip.
The problem is with all the other crazy TLDs.
I suggest you take a look at the source for ruby-domain-name
They have done a lot of work using RFCs and known data, to try and process it the right way.
So...
I've been fiddling around with this for a long time now, looking for the potential bottlenecks and after a few days of back and forth I discovered that it's actually CURL (that's waiting for the individual servers to respond with a HTTP code) that's making things slower than needed.
In the end, I opted in for a different "gethostbyname" function that takes IP6 into account to solve my problem(s).
function my_gethostbyname($host, $try_a = FALSE)
{
$dns = gethostbynamel6($host, $try_a);
if ($dns == FALSE)
{
return FALSE;
}
else
{
return $dns[0];
}
}
function gethostbynamel6($host, $try_a = FALSE)
{
$dns = array();
$dns6 = #dns_get_record($host, DNS_AAAA);
if($dns6!== FALSE)
{
$dns = array_merge($dns, $dns6);
}
if ($try_a == TRUE)
{
$dns4 = #dns_get_record($host, DNS_A);
if($dns4!== FALSE)
{
$dns = array_merge($dns, $dns4);
}
}
else
{
$dns = $dns6;
}
$ip6 = array();
$ip4 = array();
foreach ($dns as $record)
{
if ($record["type"] == "A")
{
$ip4[] = $record["ip"];
}
if ($record["type"] == "AAAA")
{
$ip6[] = $record["ipv6"];
}
}
if (count($ip6) < 1)
{
if ($try_a == TRUE)
{
if (count($ip4) < 1)
{
return FALSE;
}
else
{
return $ip4;
}
}
else
{
return FALSE;
}
}
else
{
return $ip6;
}
}
As soon as the first domain-part actually resolves to an IP, (a) the domain exists and (b) is the root domain.
This spares me major time and the trick of this is that you're only as slow as your DNS resolution and some microsecs. The curl option I used before took around 3 seconds per call (sometimes up to the full timeout range I had set to 20 secs), depending on the target server's response time - if any.
The path I now chose is easy to understand: I end up with a list of resolving domains and - when needed - I can check those using curl "on demand" or using one or more CRON jobs "on interval".
I know that it's kind of a workaround, but splitting the problem into two tasks (1 = pre-check for root domain, 2 = check if domain returns expected HTTP code) makes the whole thing faster than trying to do the complete job at once using curl.
What I've learned from this...
When checking domains, try to resolve them first so you can spare yourself the timeout burden that comes with curl.
Curl is great for many tasks, but it's not the smartest way to try to do everything with it.
When you think you can't solve a problem more than you've tried to do, split the problem in two or more parts and check again. Chances are big that you'll discover a whole new world of options to enhance what you've got.
I hope this spares someone the burden of fiddling around with an alike problem for weeks. ;)
class DomainUtils {
function __construct(){
//only these super domains
$this->superDomains = array(
'.com',
'.gov',
'.org',
'.co.uk',
'.info',
'.co',
'.net',
'.me',
'.tv',
'.mobi'
);
}
//get super domain
public function getMainDomain($domain = null){
$domainChunks = explode('.', str_ireplace($this->superDomains, '', $domain));
if(sizeof($domainChunks) == 0){
return false;
}
foreach($domainChunks as $key => $domainChunk){
if($key < sizeof($domainChunks) - 1){
$domain = str_ireplace($domainChunk . '.', '', $domain);
}
}
return $domain;
}
}