phpでmhtファイルを生成するには？

## 概要

phpのみでmhtファイルを作成したい場合
以下のライブラリで実現できます
http://www.phpkode.com/scripts/item/mht-file-maker/

## ライブラリの使い方

例えば "./cont/index.htm" ファイルをトップページとして
"./cont/" フォルダ配下のファイルを全部含むようにして
"./mht/cont.mht" ファイルを生成したい場合は…

```php:make_mht.php
$MhtFileMaker = new MhtFileMaker();
$MhtFileMaker->SetFrom('foobar <foobar@mailinator.com>');
$MhtFileMaker->SetSubject( 'any subject' );
$MhtFileMaker->SetBaseDir( './cont/' );
$MhtFileMaker->SetFirstPage('index.htm');
$MhtFileMaker->AutoAddFiles();
$MhtFileMaker->MakeFile( './mht/cont.mht' );
```
この程度のコードで実現することができます

## 補足1

トップページから関連するファイルへのリンクは相対パスになっている必要があります
相対パスになっていないものはライブラリ側で面倒を見てくれません。

## 補足2

先に上げたURLに公開されているライブラリは
生成するmhtファイルと同等のメモリを消費してしまうため
巨大なmhtファイルが生成出来ないという問題があります

これはファイルポインタ周りをいじれば改善するので
メモリ節約パッチを当てたサンプルを張っておきます

```php:mht.lib.php
<?php
/***********************************************************************
Class:    Mht File Maker
Version:   1.2 beta
Date:     02/11/2007
Author:    Wudi <wudicgi@yahoo.de>
Description: The class can make .mht file.
***********************************************************************/

/***********************************************************************
Date:        05/07/2014
Author:      zarchis
Description: Memory saving patch
***********************************************************************/

/***********************************************************************
::usage::

require_once 'mht.lib.php';

$MhtFileMaker = new MhtFileMaker();

$MhtFileMaker->SetFrom('Wudi <wudipro@yahoo.de>');
$MhtFileMaker->SetSubject('Example');

$MhtFileMaker->SetBaseDir('webpages');
$MhtFileMaker->SetFirstPage('index.htm');

$MhtFileMaker->AutoAddFiles();

$MhtFileMaker->MakeFile('example.mht');

echo '<a href="example.mht" target=_blank>Finished.</a>';

***********************************************************************/


class MhtFileMaker{
  var $config = array();
  var $headers = array();
  var $headers_exists = array();
  var $files = array();
  var $boundary;
  var $dir_base;
  var $page_first;

  function MhtFile($config = array()){

  }

  function SetHeader($header){
    $this->headers[] = $header;
    $key = strtolower(substr($header, 0, strpos($header, ':')));
    $this->headers_exists[$key] = TRUE;
  }

  function SetFrom($from){
    $this->SetHeader("From: $from");
  }

  function SetSubject($subject){
    $this->SetHeader("Subject: $subject");
  }

  function SetDate($date = NULL, $istimestamp = FALSE){
    if ($date == NULL) {
      $date = time();
    }
    if ($istimestamp == TRUE) {
      $date = date('D, d M Y H:i:s O', $date);
    }
    $this->SetHeader("Date: $date");
  }

  function SetBoundary($boundary = NULL){
    if ($boundary == NULL) {
      $this->boundary = '--' . strtoupper(md5(mt_rand())) . '_MULTIPART_MIXED';
    } else {
      $this->boundary = $boundary;
    }
  }

  function SetBaseDir($dir){
    $this->dir_base = str_replace("\\", "/", realpath($dir));
  }

  function SetFirstPage($filename){
    $this->page_first = str_replace("\\", "/", realpath("{$this->dir_base}/$filename"));
  }

  function AutoAddFiles(){
    if (!isset($this->page_first)) {
      exit ('Not set the first page.');
    }
    $filepath = str_replace($this->dir_base, '', $this->page_first);
    $filepath = 'http://mhtfile' . $filepath;
    $this->AddFile($this->page_first, $filepath, NULL);
    $this->AddDir($this->dir_base);
  }

  function AddDir($dir){
    $handle_dir = opendir($dir);
    while ($filename = readdir($handle_dir)) {
      if (($filename!='.') && ($filename!='..') && ("$dir/$filename"!=$this->page_first)) {
        if (is_dir("$dir/$filename")) {
          $this->AddDir("$dir/$filename");
        } elseif (is_file("$dir/$filename")) {
          $filepath = str_replace($this->dir_base, '', "$dir/$filename");
          $filepath = 'http://mhtfile' . $filepath;
          $this->AddFile("$dir/$filename", $filepath, NULL);
        }
      }
    }
    closedir($handle_dir);
  }

  function AddFile($filename, $filepath = NULL, $encoding = NULL){
    if ($filepath == NULL) {
      $filepath = $filename;
    }
    $mimetype = $this->GetMimeType($filename);
    $this->AddContents($filepath, $mimetype, $filename, $encoding);
  }

  function AddContents($filepath, $mimetype, $filename, $encoding = NULL){

    $this->files[] = array('filepath' => $filepath,
                'mimetype' => $mimetype,
                'filename' => $filename,
                'encoding' => $encoding);
  }

  function CheckHeaders(){
    if (!array_key_exists('date', $this->headers_exists)) {
      $this->SetDate(NULL, TRUE);
    }
    if ($this->boundary == NULL) {
      $this->SetBoundary();
    }
  }

  function CheckFiles(){
    if (count($this->files) == 0) {
      return FALSE;
    } else {
      return TRUE;
    }
  }

  function GetFile($fp){
    $this->CheckHeaders();
    if (!$this->CheckFiles()) {
      exit ('No file was added.');
    }
    $contents = implode("\r\n", $this->headers);
    $contents .= "\r\n";
    $contents .= "MIME-Version: 1.0\r\n";
    $contents .= "Content-Type: multipart/related;\r\n";
    $contents .= "\tboundary=\"{$this->boundary}\";\r\n";
    $contents .= "\ttype=\"" . $this->files[0]['mimetype'] . "\"\r\n";
    $contents .= "X-MimeOLE: Produced By Mht File Maker v1.0 beta\r\n";
    $contents .= "\r\n";
    $contents .= "This is a multi-part message in MIME format.\r\n";
    $contents .= "\r\n";
    
    fwrite($fp, $contents); $contents = "";
    
    foreach ($this->files as $file) {
      $filecont = file_get_contents($file['filename']);
      $encoding = $file['encoding'];
      if ($encoding == NULL) {
        $filecont = chunk_split(base64_encode($filecont), 76);
        $encoding = 'base64';
      }
    
      $contents .= "--{$this->boundary}\r\n";
      $contents .= "Content-Type: $file[mimetype]\r\n";
      $contents .= "Content-Transfer-Encoding: $encoding\r\n";
      $contents .= "Content-Location: $file[filepath]\r\n";
      $contents .= "\r\n";
      $contents .= $filecont;
      $contents .= "\r\n";
      
      fwrite($fp, $contents); $contents = "";
    }
    $contents .= "--{$this->boundary}--\r\n";
    
    fwrite($fp, $contents); $contents = "";
  }

  function MakeFile($filename){
    $fp = fopen($filename, 'w');
    $this->GetFile($fp);
    fclose($fp);
  }

  function GetMimeType($filename){
    $pathinfo = pathinfo($filename);
    switch ($pathinfo['extension']) {
      case 'htm': $mimetype = 'text/html'; break;
      case 'html': $mimetype = 'text/html'; break;
      case 'txt': $mimetype = 'text/plain'; break;
      case 'cgi': $mimetype = 'text/plain'; break;
      case 'php': $mimetype = 'text/plain'; break;
      case 'css': $mimetype = 'text/css'; break;
      case 'jpg': $mimetype = 'image/jpeg'; break;
      case 'jpeg': $mimetype = 'image/jpeg'; break;
      case 'jpe': $mimetype = 'image/jpeg'; break;
      case 'gif': $mimetype = 'image/gif'; break;
      case 'png': $mimetype = 'image/png'; break;
      default: $mimetype = 'application/octet-stream'; break;
    }
    return $mimetype;
  }
}
?>
```

## 補足3

補足２で巨大なmhtファイルを生成できるようになりましたが
ブラウザ側の都合でファイルサイズに制限があるのが実情です

100MB以上ならブラウザが非常に重くなり
300MB以上なら開くことが不可能になります

実用上は100MBが上限かなと感じています