Рубрика «PHP»

Отправка письма с вложением. Часть 2

Сегодняшний пост - по просьбам трудящихся. На форуме мне задали вопрос, как отправить по почте письмо с несколькими вложенными файлами. Поскольку такая заметка на блоге уже есть, то сегодня - продолжение. Форма для отправки письма с тремя вложенными файлами выглядит так:

Количество файлов, которые прикрепляются к письму не ограничено. Просто добавляем в форму столько полей для загрузки файлов, сколько нужно:

Файл: <input type="file" name="file[]" />
Файл: <input type="file" name="file[]" />
Файл: <input type="file" name="file[]" />

Ну и собственно сам скрипт:

<?php
session_start();
$admin = 'admin@mail.ru';

if ( isset( $_POST['sendMail'] ) ) {
  $name  = substr( $_POST['name'], 0, 64 );
  $email   = substr( $_POST['email'], 0, 64 );
  $subject = substr( $_POST['subject'], 0, 64 );
  $message = substr( $_POST['message'], 0, 250 );
 
  $error = '';
  if ( empty( $name ) ) $error = $error.'<li>Не заполнено поле "Имя"</li>';
  if ( empty( $email ) ) $error = $error.'<li>Не заполнено поле "E-mail"</li>';
  if ( empty( $subject ) ) $error = $error.'<li>Не заполнено поле "Тема"</li>';
  if ( empty( $message ) ) $error = $error.'<li>Не заполнено поле "Сообщение"</li>';
  if ( !empty( $email ) and !preg_match( "#^[0-9a-z_\-\.]+@[0-9a-z\-\.]+\.[a-z]{2,6}$#i", $email ) )
    $error = $error.'<li>поле "E-mail" должно соответствовать формату somebody@somewhere.ru</li>';
  if ( !empty( $error ) ) {
    $_SESSION['sendMailForm']['error']   = '<p>При заполнении формы были допущены ошибки:</p><ul>'.$error.'</ul>';
    $_SESSION['sendMailForm']['name']    = $name;
    $_SESSION['sendMailForm']['email']   = $email;
    $_SESSION['sendMailForm']['subject'] = $subject;
    $_SESSION['sendMailForm']['message'] = $message;
    header( 'Location: '.$_SERVER['PHP_SELF'] );
    die();
  }
 
  $filepath = array();
  $filename = array();
  for( $i = 0; $i < count($_FILES['file']); $i++) {
    if ( !empty( $_FILES['file']['tmp_name'][$i] ) and $_FILES['file']['error'][$i] == 0 ) {
      $filepath[] = $_FILES['file']['tmp_name'][$i];
      $filename[] = $_FILES['file']['name'][$i];
    }
  }
 
  $body = "АВТОР:\r\n".$name."\r\n\r\n";
  $body .= "E-MAIL:\r\n".$email."\r\n\r\n";
  $body .= "ТЕМА:\r\n".$subject."\r\n\r\n";
  $body .= "СООБЩЕНИЕ:\r\n".$message;
 
  if ( send_mail($admin, $body, $email, $filepath, $filename) )
    $_SESSION['success'] = true;
  else
    $_SESSION['success'] = false;
  header( 'Location: '.$_SERVER['PHP_SELF'] );
  die();
}

// Вспомогательная функция для отправки почтового сообщения с вложением
function send_mail($admin, $body, $email, $filepath, $filename)
{
  $subject = '=?windows-1251?B?'.base64_encode('Заполнена форма на сайте').'?=';
  $boundary = "--".md5(uniqid(time())); // генерируем разделитель
  $headers = "From: ".strtoupper($_SERVER['SERVER_NAME'])." <".$email.">\r\n";   
  $headers .= "Return-path: <".$email.">\r\n";
  $headers .= "MIME-Version: 1.0\r\n";
  $headers .="Content-Type: multipart/mixed; boundary=\"".$boundary."\"\r\n";
  $multipart = "--".$boundary."\r\n";
  $multipart .= "Content-type: text/plain; charset=\"windows-1251\"\r\n";
  $multipart .= "Content-Transfer-Encoding: quoted-printable\r\n\r\n";

  $body = quoted_printable_encode( $body )."\r\n\r\n";
 
  $multipart .= $body;
 
  $file = '';
  $count = count( $filepath );
  if ( $count > 0 ) {
    for ( $i = 0; $i < $count; $i++ ) {
      $fp = fopen($filepath[$i], "r");
      if ( $fp ) {
        $content = fread($fp, filesize($filepath[$i]));
        fclose($fp);
        $file .= "--".$boundary."\r\n";
        $file .= "Content-Type: application/octet-stream\r\n";
        $file .= "Content-Transfer-Encoding: base64\r\n";
        $file .= "Content-Disposition: attachment; filename=\"".$filename[$i]."\"\r\n\r\n";
        $file .= chunk_split(base64_encode($content))."\r\n";     
      }
    }
  }
  $multipart .= $file."--".$boundary."--\r\n";

  if( mail($admin, $subject, $multipart, $headers) )
    return true;
  else
    return false;
}

function quoted_printable_encode ( $string ) {
   // rule #2, #3 (leaves space and tab characters in tact)
   $string = preg_replace_callback (
   '/[^\x21-\x3C\x3E-\x7E\x09\x20]/',
   'quoted_printable_encode_character',
   $string
   );
   $newline = "=\r\n"; // '=' + CRLF (rule #4)
   // make sure the splitting of lines does not interfere with escaped characters
   // (chunk_split fails here)
   $string = preg_replace ( '/(.{73}[^=]{0,3})/', '$1'.$newline, $string);
   return $string;
}

function quoted_printable_encode_character ( $matches ) {
   $character = $matches[0];
   return sprintf ( '=%02x', ord ( $character ) );
}
?>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Отправить письмо</title>
</head>
<body>

<?php
if ( isset( $_SESSION['sendMailForm'] ) ) {
  echo $_SESSION['sendMailForm']['error'];
  $name    = htmlspecialchars ( $_SESSION['sendMailForm']['name'] );
  $email   = htmlspecialchars ( $_SESSION['sendMailForm']['email'] );
  $subject = htmlspecialchars ( $_SESSION['sendMailForm']['subject'] );
  $message = htmlspecialchars ( $_SESSION['sendMailForm']['message'] );
  unset( $_SESSION['sendMailForm'] );
} else {
  $name  = '';
  $email   = '';
  $subject = '';
  $message = '';
}

if ( isset( $_SESSION['success'] ) ) {
  if ( $_SESSION['success'] )
    echo '<p>Письмо успешно отправлено</p>';
  else
    echo '<p>Ошибка при отправке письма</p>';
  unset( $_SESSION['success'] );
}
?>

<form action="<?php echo $_SERVER['PHP_SELF'] ?>" method="POST" enctype="multipart/form-data">
<table>
<tr><td>Имя:</td><td><input type="text" name="name" maxlength="64" value="<?php echo $name ?>" /></td></tr>
<tr><td>E-mail:</td><td><input type="text" name="email" maxlength="64" value="<?php echo $email ?>" /></td></tr>
<tr><td>Тема:</td><td><input type="text" name="subject" maxlength="64" value="<?php echo $subject ?>" /></td></tr>
<tr><td>Сообщение:</td><td><textarea name="message" rows="5" cols="30"><?php echo $message ?></textarea></td></tr>
<tr><td>Файл:</td><td><input type="file" name="file[]" /></td></tr>
<tr><td>Файл:</td><td><input type="file" name="file[]" /></td></tr>
<tr><td>Файл:</td><td><input type="file" name="file[]" /></td></tr>
<tr><td>&nbsp;</td><td><input type="submit" name="sendMail" value="Отправить" /></td></tr>
</table>
</form>

</body>
</html>

Ссылки по теме:

Загрузка больших файлов на сайт

Довольно часто на форумах можно встретить вопрос: “Как залить через WEB интерфейс файл размером 10 Mb? При попытках это сделать возникают какие-то ошибки“. Если вы столкнулись с аналогичной проблемой, то данная заметка именно для вас.

Конфигурационный файл PHP php.ini имеет несколько директив, связанных с загрузкой файлов на сервер:

  • file_uploads=On — разрешает загрузку файлов на сервер по протоколу HTTP;
  • upoad_tmp_dir=/tmp — устанавливает каталог для временного хранения загруженных файлов;
  • upload_max_filesize=2M — устанавливает максимальный объем загружаемых файлов;
  • post_max_size=2M — устанавливает максимальный допустимый размер POST-данных. Это свойство также влияет на закачиваемые файлы. Чтобы закачивать большие файлы, это значение должно быть выше upload_max_filesize.

Если в вашем конфигурационном скрипте включено ограничение памяти, memory_limit также будет влиять на закачивание файлов. Проще говоря, memory_limit должна быть больше чем post_max_size.

Итак, редактируем файл php.ini:

post_max_size=10M
upload_max_filesize=10M

Также бывает полезно увеличить значение переменных PHP max_input_time и max_execution_time (время приема данных и выполнения скрипта):

max_input_time=600
max_execution_time=600

Ссылки по теме:

Создание RSS-ленты новостей

Формат RSS фактически является XML-форматом, а значит мы можем использовать возможности расширения DOM для создания RSS-ленты. Приведенный ниже код расширяет класс DOMDocument, чтобы построить дерево DOM, создавая элементы и добавляя их в соответствующую структуру.

<?php
class rss2 extends DOMDocument {
  private $channel;
 
  public function __construct($title, $link, $description) {
    parent::__construct();
    $this->formatOutputtrue;
   
    $root = $this->appendChild($this->createElement('rss'));
    $root->setAttribute('version', '2.0');
   
    $channel = $root->appendChild($this->createElement('channel'));
   
    $channel->appendChild($this->createElement('title', $title));
    $channel->appendChild($this->createElement('link', $link));
    $channel->appendChild($this->createElement('description', $description));
   
    $this->channel = $channel;
  }
 
  public function addItem($title, $link, $description) {
    $item = $this->createElement('item');
    $item->appendChild($this->createElement('title', $title));
    $item->appendChild($this->createElement('link', $link));
    $item->appendChild($this->createElement('description', $description));
    $this->channel->appendChild($item);
  }
}
?>

Конструктор класса создает и инициализирует элементы <rss> и <channel>. Он принимает три аргумента: название канала, ссылку и описание. Конструктор вызывает метод parent::__construct(), что фактически приводит к вызову метода DOMDocument::__construct(). После этого можно приступать к построению документа.

Сперва устанавливаем атрибут formatOutput в значение true. В вывод будут добавлены отступы и символы перевода строки, так что его будет легче читать.

Затем создаем корневой элемент документа, т.е rss, и устанавливаем атрибут version в значение 2.0, потому как мы строим ленту новостей формата RSS 2.0.

Все данные будут находиться внутри элемента channel под узлом rss, поэтому следующий шаг - создание этого элемента и инициализации его элементов-потомков title, link и description.

Все эти данные берутся из аргументов, переданных конструктору. Они создаются методом CreateElement(), который позволяет указать как имя элемента, так и текстовой узел за один вызов.

Наконец, сохраняем элемент channel, чтобы упростить обращение к нему в дальнейшем.

Для добавления новых статей предназначен метод addItem(). Поскольку элементы item содержат те же данные, что и channel, код метода addItem() почти идентичен коду конструктора.

Пример использования класса:

<?php
$dblocation = 'localhost';   // Имя сервера
$dbuser     = 'root';        // Имя пользователя
$dbpswrd    = '';            // Пароль
$dbname     = 'mydb';        // Имя базы данных

// Соединение с сервером базы данных
$dblink = mysql_connect( $dblocation, $dbuser, $dbpswrd );
mysql_query( 'SET NAMES UTF8' );
// Выбираем базу данных
mysql_select_db( $dbname, $dblink );

$rss = new rss2('Новости по системам безопасности', 'http://sec-news.ru',
                'Российские новости по техническим средствам и системам безопасности
                (видеонаблюдение, контроль доступа, охранно-пожарная сигализация)'
);

$query = 'SELECT id, title, announce
          FROM news
          ORDER BY puttime DESC
          LIMIT 10'
;
$res = mysql_query( $query );
               
while( $news = mysql_fetch_array( $res ) ) {
  $rss->addItem($news['title'], 'http://sec-news.ru/news/'.$news['id'].'/', $news['announce']);
}

echo $rss->saveXML();
?>