PHPから Excel用 UTF8のCSVの出力

PHPから Excel用 UTF8のCSVを出力します。

  • Excelで開きたい。
  • 汎用のCSV形式にしたい。Excel専用にはしたくない。
  • 改行が含まれるデータが存在する。
  • UTF-8 のままで出力したい。Shift-JISへ変換できない文字が含まれている。

※ ExcelのCSVはShift-JISで、改行コードが混在します。ファイルをダブルクリックで開く場合と、テキストファイルの読み込みで開く場合とで挙動が異なります。

解決策

この2つがポイントです。

  • 拡張子を.csvにする
  • 先頭にUTF8を宣言するBOMを付ける

実際のコードです。

<?php
header("Cache-Control: public");
header("Pragma: public");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=data.csv");

//BOM
echo pack('C*',0xEF,0xBB,0xBF);

//データはここから
foreach($rows as $row){
    echo ・・・
}

TCPDFの地雷 このページにはエラーがあります。Acrobatはページを正しく表示できない場合があります。PDF文書の作成者に連絡して、問題を解決してください。

adobe-error.fw

TCPDF の writeHTML() でPDFを作成している場合に発生します。Adobe製品だけエラーメッセージでPDFが表示できず、Chromeで開発していると正常表示の為気づかないという、なんともおかしな現象です。

原因

わかりません。。

HTMLの構造や属性で発生するようです。

対策

体当たり的に経験値を上げるしかありません。。

エラーを経験したHTMLコード

  • <table align=”right”> 「align=”right”」を削除すると正常になった
  • <div><table> 属性なしの場合 <div>を削除すると正常になった
  • <div><div> 属性なしの場合 <div><div>を削除すると正常になった

TCPDF の writeHTML() で background-image

具体的に言うと

  • 請求書PDF等をPHPから出力したい
  • PHPでゴニョゴニョ書くのは面倒なので、レイアウトはHTMLで管理したい
  • 角印の背景画像を入れたい

今回は、TCPDFのwriteHTML()で背景画像をなんとかする方法です。

background-image: ⇒ ダメ
margin-top: ⇒ ダメ
position: ⇒ ダメ

...そして、 stack overflow : TCPDF not render all CSS properties を見たりして、あきらめかけている方に朗報です。

なぜか唯一 table タグ の padding だけサポートされています。

 

こんな感じで解決です。
<img src=”stamp.jpg” />
<table style=”padding-top:-100px;”><tr><td>
    株式会社日本ユニバース<br />
    http://nusoft.jp/
</td></tr></table>

Zend_DB Fatal error: Uncaught exception ‘Zend_Db_Table_Exception’ with message ‘A table must have a primary key

Fatal error: Uncaught exception ‘Zend_Db_Table_Exception’ with message ‘A table must have a primary key …

MySQLのViewを Zend_DB_Tableを使用して読み込んだ場合のエラーです。
主キーがない!って。そりゃそうさ、Viewだよ…じゃなくて、教えてあげましょう。

class thisIsView extends Zend_Db_Table_Abstract
{
    protected $_name = 'this_is_view';
    protected $_primary = 'id';
    protected $_sequence = false;
}

 

PHP5.4 からの upload_progress と Zend_Session

PHP5.4からsession.upload_progressが使用できるようになりました。詳しくは検索!もしくはこちら
けっこう簡単に素敵なアップロード画面を作ることができるようになりました。

ただ…
Zend_Sessionと併用はできないようです。 (↓訂正)

名前が session.upload_progress.name の値を持つPOSTを受け取った時点で、どうも session_start しているようです。

Zend_Sessionを使う場合は php_value session.auto_start 0 にして勝手にsession_startを呼ばないでねっていうZend_Sessionとの約束は守れません。

なので、Zend_Auth を認証に使ってたら new Zend_Auth_Storage_Session() で怒られます。
「session has already been started by session.auto-start or session_start() 」

ドハマリ数時間の結論は、併用不可。アップロード画面はZend_Session、Zend_Authを使用せず、別の方法で認証してupload_progressを優先することにします。(↓訂正)

— 訂正 —
なんということでしょう。結構簡単に実装できちゃいました。Zendの設計者スバラシイ。
Zend_File_Transfer_Adapter_Http  あたりが答えです。
情報は少なくなりますが、このあたり(stackoverflow)が参考になります。

cronでDB定期バックアップ メール添付編

データベースのダンプファイルを、定期的にメールに添付して送信する、という方法です。DBに直接アクセスできる怖さはみんな駆け出しのころ経験してますよね?(僕だけ?)ちなみに、ここ最近では、Zend_Db の$db->delete(); で久々に冷や汗かきました。

/usr/bin/mysqldump --opt -c -h localhost -u root --password=xxxx dbname | zip |  /usr/bin/php5 /home/www/cron/mailto.php -f dbname_dump.zip

cronからの呼び出しはこんな感じです。
で、上で指定した引数はZend_Console_Getoptで受けられます。便利ですね。

mailto.php

<?php
set_include_path(
    get_include_path() .
    PATH_SEPARATOR . '/home/www/libs/Zend'
);

require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->setFallbackAutoloader(true);

$opts = new Zend_Console_Getopt('f:');
$filename = $opts->getOption('f');

$address = 'yamashita@nusoft.jp';
$body = $filename;

$mail = new Zend_Mail();
$mail->setBodyText($body);
$mail->setFrom('yamashita@nusoft.jp', 'XServer Cron');
$mail->addTo($address, '');
$mail->setSubject('MYSQL DUMP ' . $filename);
$at = $mail->createAttachment(file_get_contents("php://stdin"));
$at->filename = $filename;
$mail->send();

お試しあれ。なお、Gmailの場合はゴミ箱へ直行するフィルタを作っておけば、30日経過後自動的に削除してくれます。

書いてて思い出しましたが、WEBプログラマーにはもっとわかりやすくてメンテしやすい方法があります。覚えておくと便利です。cronへの登録はこんな感じ。

0 0 * * * wget --spider http://nusoft.jp/dbbackup.php >/dev/null 2>&1

ポイントはリクエストするファイル内で、リクエスト元IP(同一サーバー内なら127.0.0.1 かな)からのアクセスのみ処理対象にすることです。これならいつもの感じなのでわかりやすいですね。

Google ウェブマスターツール用のsitemap.xmlをリアルタイム表示する

Google ウェブマスターツール用のsitemap.xmlをリアルタイム表示します。
PHPで書きます。ファイル名「sitemap.xml.php」としますので、まず.htaccessの設定です。

.htaccess

RewriteEngine On
RewriteRule sitemap.xml$ sitemap.xml.php [NC,L]

 

sitemap.xml.php

<?php

const URLROOT = 'http://nusoft.jp';
$files = getFiles("");

$urls = array();

foreach($files as $file){
	if(!preg_match('/_hidden_dir/i', $file)){ //対象外を設定します
		$urls[$file]['lastmod'] = date('Y-m-d',filemtime($file));
		$urls[$file]['loc'] = URLROOT . '/' . $file;
	}
}

echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
echo '<urlset xmlns="http://www.google.com/schemas/sitemap/0.84">' . "\n";
foreach($urls as $url){
	echo '<url>' . "\n";
	echo '<loc>' . $url['loc'] . '</loc>' . "\n";
	echo '<lastmod>' . $url['lastmod'] . '</lastmod>' . "\n";
	echo '</url>' . "\n";
}
echo '</urlset>' . "\n";

function getFiles($dir) {
	$list = $tmp = array();
	foreach(glob($dir . '*/', GLOB_ONLYDIR) as $child_dir) {
		if ($tmp = getFiles($child_dir)) {
			$list = array_merge($list, $tmp);
		}
	}
	foreach(glob($dir . '{*.html}', GLOB_BRACE) as $files) {
		$list[] = $files;
	}
	return $list;
}

あとは、このアドレスをGoogleウェブマスターツールに登録して待つこと1日。完了です。