-2

I am working on a mailing list.

My mail list has over 400 emails right now, so I am using foreach.

My question is: Can I limit foreach for only 100 emails?

Eg - Send an email to the first 100 emails, break, then start from where it stopped. So, for example, for 400 emails, foreach to be called 4 times to send an email to all the emails.

foreach ($addresses as $to) {
     mail($to,$subject,$message,$headers);
}

I need to do it that way because if I send the email to all 400 emails at once, I get 503 service unavailable.

I tried this but doesn't work...

$emailchunkarray = array_chunk($addresses, 50);
$arraysize = count ($emailchunkarray);
$x = 0;

function chunker() {
    if ($x <= $arraysize) {
       foreach ($emailchunkarray[$x] as $to) {
           mail($to,$subject,$message,$headers);
       }
       $x++
       $chunker2();
    }
}
$chunker();

function chunker2() {
    if ($x <= $arraysize) {
        foreach ($emailchunkarray[$x] as $to){
            mail($to,$subject,$message,$headers);
        }
        $x++;
        $chunker();
    }
}

For some reason I can't answer my question... So here is how I did it:

$ChunkAddresses = 10;
$EmailChunkArray = array_chunk($Addresses, $ChunkAddresses);
$ArraySize = count ($EmailChunkArray);
$ChunkSize = 0;
$ForeachCounter = 0;
ChunkLoop: {
    if ($GLOBALS["ChunkSize"] <= $GLOBALS["ArraySize"]) {
        $EmailChunkArrayLoop = $GLOBALS["EmailChunkArray"];
        foreach ($EmailChunkArrayLoop[$GLOBALS["ChunkSize"]] as $ToChunkLoop) {
            mail($ToChunkLoop,$GLOBALS["Subject"],$GLOBALS["Message"],$GLOBALS["Headers"]);
            $GLOBALS["ForeachCounter"]++;
                if ($GLOBALS["ForeachCounter"] == $GLOBALS["ChunkAddresses"]) {
                    echo "This is the " . $GLOBALS["ChunkSize"] . " chunk.";
                    $GLOBALS["ForeachCounter"] = 0;
                    break 1;}
        }
        $GLOBALS["ChunkSize"]++;
        sleep(3);
        goto ChunkLoopFollow;} else if ($GLOBALS["ChunkSize"] == $GLOBALS["ArraySize"]){
        exit();}
}
ChunkLoopFollow: {
    if ($GLOBALS["ChunkSize"] <= $GLOBALS["ArraySize"]) {
        $EmailChunkArrayLoopFollow = $GLOBALS["EmailChunkArray"];
        foreach ($EmailChunkArrayLoopFollow[$GLOBALS["ChunkSize"]] as $ToChunkLoopFollow) {
            mail($ToChunkLoopFollow,$GLOBALS["Subject"],$GLOBALS["Message"],$GLOBALS["Headers"]);
            $GLOBALS["ForeachCounter"]++;
                if ($GLOBALS["ForeachCounter"] == $GLOBALS["ChunkAddresses"]) {
                    echo "This is the " . $GLOBALS["ChunkSize"] . " chunk.";
                    $GLOBALS["ForeachCounter"] = 0;
                    break 1;}
        }
        $GLOBALS["ChunkSize"]++;
        sleep(3);
        goto ChunkLoop;} else if ($GLOBALS["ChunkSize"] == $GLOBALS["ArraySize"]){
        exit();}
}
echo "Finished!";
Sumutiu Marius
  • 421
  • 4
  • 18
  • 3
    make use of a database to track which mail has been sent or need to be sent. – Hyder B. Sep 04 '17 at 12:46
  • 3
    Might perhaps make more sense to use `for` loops to begin with in such a case? (But incrementing a counter inside a foreach loop, checking that counter and breaking out of the loop if applicable, is of course not rocket science either.) – CBroe Sep 04 '17 at 12:47
  • 1
    Use a task scheduler and a queue, chunk the array, etc., lots of possibilities. – jeroen Sep 04 '17 at 12:49
  • @CBroe if I do that, then when I will start foreach again, it will start $addresses from 0, not from where I broke out of the loop. – Sumutiu Marius Sep 04 '17 at 13:19
  • Well that you simply “do nothing” inside the loop once the counter is _below_ a specified threshold was implied ... – CBroe Sep 04 '17 at 13:22
  • @CBroe Tried somethin but still doesn't work... See the edit. – Sumutiu Marius Sep 04 '17 at 15:32
  • I did it. I want to answer my own question but I can't for some reason... – Sumutiu Marius Sep 04 '17 at 17:51

1 Answers1

5

Split your array with array_chunk() and then wrap your foreach with another foreach.

array_chunk($input_array, 100);

This should give you an array of 4 arrays, each of 100 elements (emails) inside it.

As that answers your question it might not help you with a problem. You have to pause your code somehow and for that I'd recommend to use AJAX as in JavaScript you can easily specify timed events. For example you can create small API in PHP that would send x emails, starting at y index where x and y are values from POST request and then control it with AJAX calls to your small API.

Another workaround would be to use Cron jobs How to create cron job using PHP?

PHP array_chunk()

Melcma
  • 608
  • 3
  • 14
  • 2
    While this splits the array like OP requested, it won’t help him around his real problem. Iterating through the new chunked arrays will still be too fast and error out. – ishegg Sep 04 '17 at 12:59
  • @ishegg exactly. I tried `foreach (array_chunk($addresses, 100) as $to){mail($to,$subject,$message,$headers);}` but it is the same thing. I need to somehow find how many chunks of 100 emails are and execute foreach multiple times for every chunk so then I won't get a timeout because of the php_max_execution_time – Sumutiu Marius Sep 04 '17 at 13:10
  • Good spot, I've added a note to my answer – Melcma Sep 04 '17 at 13:10
  • 1
    @Sumutiu Marius, run your script as background process with a sleep() function for every chunk. Otherwise you will get timeout – Michael Eugene Yuen Sep 04 '17 at 13:18
  • @MichaelEugeneYuen does sleep halts the function? I did something like this: `$emailchunkarray = array_chunk($addresses, 50); foreach ($emailchunkarray as $emailchunk){ foreach ($emailchunk as $to) { mail($to,$subject,$message,$headers); } sleep(10); };` – Sumutiu Marius Sep 04 '17 at 13:41
  • 1
    Yes, but that doesn't help your situation, sleep will pause your execution that also cause your timeout. So you would need to run background process together with it. Sleep(10) only helps you from not sending excessive mail through smtp at a time – Michael Eugene Yuen Sep 04 '17 at 13:43
  • @MichaelEugeneYuen Is there a way to get the number of chunks in an array? – Sumutiu Marius Sep 04 '17 at 13:51
  • 2
    echo count($emailchunkarray); – Melcma Sep 04 '17 at 13:52
  • $chunks = array_chunk($input_array, 100); echo count($chunks); – Michael Eugene Yuen Sep 04 '17 at 13:53
  • @Melcma I've set an counter inside the foreach then I set an if statement to check if that counter reached the number of emails in the array_chunk and if true, then it executes `break 1;` to break out of the foreach, automatically killing the foreach – Sumutiu Marius Sep 14 '17 at 05:05