简体   繁体   中英

How can I use an output buffer inside a foreach loop?

I am building a simple class that can generate html for my email body by a combination of a template include and an array of data that I extract inside an output buffer.

The problem is that when I run the method with the output buffer inside a loop (to create emails based on user information) I get the following error:

ob_end_clean(): failed to delete buffer. No buffer to delete in... Cannot modify header information - headers already sent by...

What do I have to change so it works both for a single email and inside a loop?

Email class

<?php
class Email {

   protected $_html;

   protected $_data;

   public function __construct( array $email_data = [] ) {
      $this->_data = $email_data;
      $this->generate_html();
   }

   protected function generate_html() {
      $template_path = 'path/to/email-template.php';
      ob_start();
      extract( $this->_data );
        include $template_path;
      $this->_html = ob_get_contents();
      ob_end_clean();
   }

   public send( $to = [] ) {
      mail( $to, 'Test email', $this->_html, ['Content-Type: text/html; charset=UTF-8'] );
   }

}
?>

Testing my code:

<?php 
function send_test_email( $id, $to ) {
   $email_data = [
      'id'    => $id,
      'name'  => 'Joe',
      'price' => '30.00'
   ];
   $email = new Email( $email_data );
   $email->send( $to );
}

// Works
send_test_email( 122, 'joe@example.com' );

// Does not work
// ob_end_clean(): failed to delete buffer. No buffer to delete in
// Cannot modify header information - headers already sent by ...
foreach ( $users as $user ) {
   send_test_email( $user->id, $user->email );
}
?>

Let's take a look at how output buffers work:

// Works
send_test_email( 122, 'joe@example.com' );

/*
    When you have executed send_test_email() above the method generate_html() is 
    executed and ob_start and ob_end_clean() is executed. This means that the output 
    buffer is initiated and stopped with ob_end_clean(). 

    When output buffer is ended it is sent to the client (browser).
*/

//If you do the same again...
send_test_email( 122, 'joe@example.com' );

/*
  ...this won't work because output buffer is sent to the browser. After this is done
  you are not able to modify header information. Because you cannot modify header 
  information you can not start a new output buffer either.

  That is why you get the error below:

  ob_end_clean(): failed to delete buffer. No buffer to delete in
  Cannot modify header information - headers already sent by ...
*/

To solve this issue I would try something like this:

Move out ob_start() and ob_end_clean() outside of the class and remove them from the method generate_html()

protected function generate_html() {
    $template_path = 'path/to/email-template.php';
    extract( $this->_data );
    include $template_path;
    $this->_html = ob_get_contents();
}

Testing your code:

ob_start();
send_test_email( 122, 'joe@example.com' );

foreach ( $users as $user ) {
   send_test_email( $user->id, $user->email );
}
ob_end_clean();

UPDATE You could solve this without messing around with output buffers, something like:

protected function generate_html() {
    $template_path = 'path/to/email-template.php';
    extract( $this->_data );

    //requires the template file to have a return to able to store the
    //content in a variable
    //
    //Look at $foo = include 'return.php'; 
    //at https://www.php.net/manual/en/function.include.php
    $this->_html = include $template_path; 
}
 protected function generate_html() {
      $template_path = 'path/to/email-template.php';
      ob_start();
      extract( $this->_data );
        include $template_path;
      $this->_html = ob_get_clean();  //use ob_get_clean()  instead of ob_get_contents();
   }

Hope it will help you

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM