sidewalkcafe blog

日々是好日

困った

テスト環境でデータベースからの表示に成功したのでサーバ環境でテストしてみたところ
エラーてんこもり^^;

ローカルテストでは出なかったのになんでかなと思ったら
ini_set( 'error_reporting', E_ALL ); 設定したら出たわ
警告表示レベルが低かったのね・・・

Deprecated: Function ereg() is deprecated

ereg は非推奨になったので変更すれという事か・・・

という訳でereg()はpreg_match()に、
ereg_replace()はpreg_replace()に置き換え

Fatal error: Call to undefined function password_hash()

パスワードハッシュ関連の関数はPHP5.5から
それ以前は定義されてないのでエラー

ライブラリがあるようなのでなんとかなる

Strict Standards: Non-static method htmltemplate::removeTag() should not be called statically 

staticではないクラスを静的コールしたら出るエラー
動的コールに直すかstaticを設定


phpのバージョン上げられれば良いんだけど
他のプログラムも書き換えないと動かなくなりそうやしなあ・・・

やっててよかったajax

今は投稿内容をapiで吸い出したjsonデータに変換してajaxで検索してたんだけど
これ、like検索したほうが精度高いしいろいろ設定楽そう

select * from bbs where comment like '検索語'

まあどのみちdb化したらjsから読めなくなるので改造は必須だけど

$(function() {
 $.ajax({
  url: api.php,
  success: function() {
    $.getJSON(data.json,
       function(data) {
        検索+表示処理
       })})}

こんな感じで書いてたけど

$.getJSON(api.php,
    function(data) {
        表示処理
    })

これで済みそう


まあpotiboard.php本体に組み込んだ方が話早いんだろうけど
処理を軽くするって意図があるから使える者は使うというアレ

暗黙の型変換はそんなに遅いのか

前に出た話題
暗黙の方変換の速度比較

//繰り返し
$r = 1000000 ;
//時間計測1
$start = microtime(true);
for($i = 0; $i < $r; $i++) {
	$d = "0";
	$d ++ ;
}
$end = microtime(true);
echo "処理時間1:" . ($end - $start) . "秒
"; //時間計測2 $start = microtime(true); for($i = 0; $i < $r; $i++) { $d ='0'; $d = (integer)$d; $d ++ ; } $end = microtime(true); echo "処理時間2:" . ($end - $start) . "秒
"; //時間計測3 $start = microtime(true); for($i = 0; $i < $r; $i++) { $d =0; $d ++ ; } $end = microtime(true); echo "処理時間3:" . ($end - $start) . "秒
";

1=約0.13秒
2=約0.16秒
3=約0.10秒


あれ、そんな違わない・・・
というかいちいち型変換してた方が遅い・・・?


という訳で追試

//繰り返し
$r = 1000000 ;
//時間計測1
$start = microtime(true);
for($i = 0; $i < $r; $i++) {
	$d = "0";
	if($d == 0){$d ++ ;}
}
$end = microtime(true);
echo "処理時間1:" . ($end - $start) . "秒
"; //時間計測2 $start = microtime(true); for($i = 0; $i < $r; $i++) { $d ='0'; $d = (integer)$d; if($d == 0){$d ++ ;} } $end = microtime(true); echo "処理時間2:" . ($end - $start) . "秒
"; //時間計測3 $start = microtime(true); for($i = 0; $i < $r; $i++) { $d =0; if($d == 0){$d ++ ;} } $end = microtime(true); echo "処理時間3:" . ($end - $start) . "秒
";

1=約0.20秒
2=約0.18秒
3=約0.10秒


比較が入ると違うなー
そして安定の無変換
最初から数字指定しとけと


でもSQliteのデータは数字指定でも文字で記録されてるとか、potiboardのconfigは定数の指定が数字でも文字(括弧付き)になってるとか

やってて良かったSQLite

範囲呼び出しの話
SQliteには BETWEEN というそのままな呼び出しコマンドがあった

select * from bbs where id BETWEEN 1 and 20

別にwhereで囲んでも結果は同じなんだけど、どっちが早いんだろ?

select * from bbs where id > 1 and id < 20


親記事の数を数える話
もとはtree.logの行数をcount()してたけど
sqlの場合、count(*)を使う

select count(*) from bbs where parent > 0

値の取得はforeachじゃなくて、1行取得のfetchを使うらしい

$query->fetchColumn();


レスコメント表示の話
DSP_RESで一覧画面のレス表示件数、DSP_RESIMGでレス内の画像表示件数が指示されてるので
レス取得のクエリに limitをつける

select * from bbs where tree == $res limit DSP_RES

ただ、クエリ内で定数が使えなかったので変数に格納して呼び出す


画像表示件数は画像をカウントして指定数になるとbreakする

$c_img = (DSP_RES)? 0:null;
if($filename) $c_img ++
if($c_img >= DSP_RES)break;

という訳で三項演算子
ifより少しばかり遅いけど記載が短くなるから好き


って三項演算子の速度比較みてたら

三項演算子よりも暗黙の型変換のほうが、速度低下が大きい

なんですとー!!

$a = '0';
$a ++

こういう記載よくやるけど駄目なんか

POTI-boardのDB化2

SQLiteからデータを呼び出す場合
1.まとめて呼び出して処理しながら配列に入れる
2.その都度必要なデータを呼び出して処理する


どっちの方が早いのかなと
まあ、大量データを扱うなら、PHPで配列操作するよりsqlで個々に呼び出した方が早い気がする


という事で
呼び出し1

<?php
try {
	//db接続
	$db = new PDO('sqlite:log/boardlog.db' );
	
	//ページ数
	$p=0;
	//1ページの記事数
	$page_def = 20;
	//現在ページの記事範囲
	$page_r = $p * $page_def ;
	//ページおわり
	$page_e = $page_r + $page_def ;
	
	//計測開始
	$start = microtime(true);
	//100回繰り返す
	for($i = 0; $i < 100; $i++) {
	//dql呼び出し文
	$sql = "select * from oekakibbs where invz == 0 order by parent asc, tree desc, id asc";
	
	//メインデータ変数
	$dat = array();
	//データ分割
	foreach ($db->query($sql) as $data) {
		//親データ取得
		$parent = $data['parent'];
		//ページおわり
		if($parent > $page_e){ break;}
		//現在ページ迄処理を飛ばす
		if ($parent == 0 ){ $comm_res[] = $data ; continue; }
		if ($parent <= $page_r ){continue ;}
		//親データ格納
		$dat['oya'][] = $data ;
		
		//親記事番号
		$oya = $parent -1;
		//子データ格納
		foreach ($comm_res as $res) {
			if($res['tree'] != $data['tree']){continue;}
			$dat['oya'][$oya][] = $res ; 
		}
	}
	}
	
	$end = microtime(true);
echo "処理時間:" . ($end - $start) . "秒";

	//db切断
    $db = null;
  } catch (PDOException $e) {
    echo "<p>エラー:", $e->getMessage(), "</p>";
}

約5.3病でした


その2

<?php

try {
	//db接続
	$db = new PDO('sqlite:log/boardlog.db' );
	
	
	//ページ数
	$p=0;
	//1ページの記事数
	$page_def = 20;
	//現在ページの記事範囲
	$page_r = $p * $page_def ;
	//ページおわり
	$page_e = $page_r + $page_def ;
	
	
	//計測開始
	$start = microtime(true);
	
	for($i = 0; $i < 100; $i++) {
	//dql呼び出し文
	$sql = "select * from oekakibbs where invz == 0 and parent > 0 order by parent asc";
	
	//メインデータ変数
	$dat = array();
	//データ分割
	foreach ($db->query($sql) as $data) {
	
		//親データ取得
		$parent = $data['parent'];
		//ページおわり
		if($parent > $page_e){ break;}
		//現在ページ迄処理を飛ばす
		if ($parent <= $page_r ){continue ;}
		
		$dat['oya'][] = $data ;
		
		//親記事
		$oya = $parent -1;
		
		$tree_res = $data['tree']; 
		
		$sql2 = "select * from oekakibbs where invz == 0 and tree == $tree_res order by id desc";
		
		foreach ($db->query($sql2) as $res) {
			$dat['oya'][$oya][] = $res ; 
		}
	}
	}
	$end = microtime(true);
echo "処理時間:" . ($end - $start) . "秒";

	//db切断
    $db = null;
  } catch (PDOException $e) {
    echo "<p>エラー:", $e->getMessage(), "</p>";
}

約0.4秒!!


全然違うね〜
やっぱり毎度sqlで呼び出した方が早い


よく考えたらwhere で parent > 開始NO and parent <=終了no とした方が早いか

POTI-boardの警告エラー修正

POTI-boardで良くあるエラーが

Notice: Undefined variable: 変数 in 〜
//定義してない変数を使う

Notice: Undefined offset: 配列のキー in
//定義してない配列の値を使う

Notice: Undefined index: 連想配列 in
//定義してない連想配列を使う

エラーや警告が出ると(サーバー上で表示されなくても)速度がめっちゃ落ちるので
細かく改善していった方が良い


なので
例えばアップロードされるファイルの有る無しは

if(isset($_FILES["upfile"])){
	$upfile_name=$_FILES["upfile"]["name"];
	$upfile=$_FILES["upfile"]["tmp_name"];
}

で取得する


他にも

switch($mode){}
の前に
if (isset($mode)){}
を入れて囲む


関数の場合、参照渡しとデフォルト引数を設定することで呼び出しもとの変数が空でも警告がでなくなる

例えば記事上書きの場合
case 'rewrite':
	rewrite($no,$name,$email,$sub,$com,$url,$pwd,$admin);
	break;

読者編集だと$adminが設定されてないので
関数の方に「&$admin=""」と入れて空の場合でも変数宣言できるようにする

function rewrite($no,$name,$email,$sub,$com,$url,$pwd,&$admin =''){}

これで$adminがあればその値に、なければ空の値が入る

単純に
if($a){
で警告が出てる場合は
if(isset($a) && $a){
としても良い

POTI-boardのDB化1

POTI-boardのログファイルをデータベースに書き込み

こんな幹事のテーブルをつくる

id:コメントid
no:記事no(あとでidと結合削除)
date:日時
name:投稿者名
mail:メールアドレス(or sage)
subject:題名
comment:コメント
url:URL
host:接続ホスト名(ipアドレス)
exid:ユーザーID
pwd:パスワード
ext:画像ファイルの拡張子
img_w:画像幅
img_h:画像高
filename:画像ファイル名
thumbnail:サムネファイル名
cdt:チェックデジット(必要?)
time:お絵描き時間
fontcolor:文字色
tree:親ツリー番号(親はid==treeになる)
parent:親表示順(子はparent=0になる)
invz:記事非表示(invz=1のコメントは表示しない)

img.logの内容に、tree,parent,invzを追加する
tree,parentは、tree.logの内容を1つにまとめるためのもの
invzは削除した記事を表示させないためのもの
(補足)通常、読者が消したコメントを保存しておくことは色々と危うい
ただ、荒らしを通報する場合にipやコメント内容は記録してた方が良いので、管理人削除の場合invz=1として一般からは見えない様にする


ログ呼び出し、テーブル作成、挿入

<?php
//ログファイルの設定
//掲示板のconfigから取って来たのでまわりくどいw
define('LOGFILE', './log/img.log');
define('TREEFILE', './log/tree.log');

//ちゃんと動いてるか確認するための表示
echo("データ更新中");

//データを入れておくための変数
$arrayDATA = array();

//log読み込み
$lines = file(LOGFILE, FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES);
$treelines = file(TREEFILE, FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES);

//logを各行毎に読み込み
foreach ($lines as $line) {
	//','で分割、それぞれ変数に格納
	list($no, $now, $name, $email, $sub, $com, $url,
		$host, $pwd, $ext, $w, $h, $time, $chk, $ptime, $fcolor)
			= explode(',', $line);
	//$nowにidと日付がくっついて入ってるので分割
	$exnow =  explode(" ID:", $now);
	//ファイル名とサムネイル名作成
	$exfilename = $ext ? $time.$ext : "";
	$thumbnail = $ext ? $time."s.jpg" : "";

	//親子判定
	$ti = 1;
	$mst = 0;
	foreach ($treelines as $treeline){
		$treelist = explode(",", $treeline);
		if(in_array($no, $treelist)){
			$extree = $treelist[0];
			if($extree == $no){
				$mst = $ti ;
			}
			break;
		}//if treelist extree
		$ti++;
	}//foreach treelines
					
	//各項目を配列に格納
	array_push($arrayDATA, array(
		'no' => $no,			// 記事No
		'date' => $exnow[0],	// 投稿日時
		'name' => $name,		// 投稿者名
		'mail' => $email,		// メールアドレス
		'subject' => $sub,		// 記事タイトル
		'comment' => $com,		// コメント内容
		'url' => $url,			// 投稿者URL
		'host' => $host,		// 投稿者ホスト
		'exid' => $exnow[1],	// 投稿者ID
		'pass' => $pwd,			// パスワード
		'ext' => $ext,			// 投稿画像の拡張子
		'img_w' => $w,			// 投稿画像サイズ
		'img_h' => $h,			// 投稿画像サイズ
		'filename' => $exfilename,	// 画像ファイル名
		'thumbnail' => $thumbnail,	// サムネイル
		'check' => $chk,			// 投稿画像のチェックデジット
		'time' => $ptime,	// お絵かきにかかった時間
		'fontcolor' => $fcolor,	// 投稿者の文字色
		'tree' => $extree, //親ツリー
		'parent' => $mst, //親ツリー順番
	));

} //foreach lines

//配列の順序を逆順にする
$arrayDATA = array_reverse($arrayDATA);

//データベース宣言
try {
    $db = new PDO('sqlite:log/boardlog.db' );

    // テーブル作成
    $db->exec("CREATE TABLE IF NOT EXISTS oekakibbs(id integer primary key,no integer,date text,name text,mail text,subject text,comment text,url text,host text,exid text,pwd text,ext text,img_w text,img_h text,filename text,thumbnail text,cdt text,time text,fontcolor text,tree integer,parent integer,invz integer)");


    // 挿入(プリペアドステートメント)
    $s = $db->prepare("INSERT INTO oekakibbs(no ,date ,name ,mail ,subject ,comment ,url ,host ,exid ,pwd ,ext ,img_w ,img_h ,filename ,thumbnail ,cdt ,time ,fontcolor ,tree ,parent ,invz)
	 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
    
      $s->bindParam(1, $db1);
      $s->bindParam(2, $db2);
      $s->bindParam(3, $db3);
      $s->bindParam(4, $db4);
      $s->bindParam(5, $db5);
      $s->bindParam(6, $db6);
      $s->bindParam(7, $db7);
      $s->bindParam(8, $db8);
      $s->bindParam(9, $db9);
      $s->bindParam(10, $db10);
      $s->bindParam(11, $db11);
      $s->bindParam(12, $db12);
      $s->bindParam(13, $db13);
      $s->bindParam(14, $db14);
      $s->bindParam(15, $db15);
      $s->bindParam(16, $db16);
      $s->bindParam(17, $db17);
      $s->bindParam(18, $db18);
      $s->bindParam(19, $db19);
      $s->bindParam(20, $db20);
      $s->bindParam(21, $db21);

//削除があると記事NOが飛ぶので空白追加ルーチンを入れる
$i =1;
foreach ($arrayDATA as $data){
$db1 = $data['no'];
//idとnoが合わなければ空白行を追加
if($db1 > $i ){ 
	$db2 = $db4 = $db7 = $db9 = $db11 = $db12 = $db13 = $db14 = $db15 = $db16 = $db17 = $db18 = '';
	$db3 = $db5 = $db6 = $db8 = $db10 = 'delete';
	$db19 = $db20 = 0;
	$db21 =1;
	for ($j = $i; $j <= $data['no']; $j++) {
		if($data['no'] == $i ){break;}
		$db1 = $i;
		$s->execute();
		$i++;
	}
}
$invz = 0;


$db1 = $data['no'];
$db2 = $data['date'];
$db3 = $data['name'];
$db4 = $data['mail'];
$db5 = $data['subject'];
$db6 = $data['comment'];
$db7 = $data['url'];
$db8 = $data['host'];
$db9 = $data['exid'];
$db10 = $data['pass'];
$db11 = $data['ext'];
$db12 = $data['img_w'];
$db13 = $data['img_h'];
$db14 = $data['filename'];
$db15 = $data['thumbnail'];
$db16 = $data['check'];
$db17 = $data['time'];
$db18 = $data['fontcolor'];
$db19 = $data['tree'];
$db20 = $data['parent'];
$db21 = $invz;

//dbに追加
      $s->execute()
        or die("<p>書き込みに失敗しました</p>");//失敗処理
	$i ++;
}//追加処理おわり

//追加したデータを表示
//sql呼び出し
$sql = "select * from oekakibbs order by id desc";

//テーブル作成
echo "<table border=\"1\">\n";
echo "<tr><th>DBid</th><th>番号</th><th>日時</th><th>Mail</th><th>題名</th><th>コメント</th><th>URL</th><th>host</th><th>USERID</th><th>Pass</th><th>拡張子</th><th>画像幅</th><th>画像高</th><th>ファイル名</th><th>サムネ</th><th>check</th><th>時間</th><th>カラー</th><th>ツリー</th><th>記事順</th><th>削除</th></tr>\n";

//各コメントデータ表示
foreach ($db->query($sql) as $a) {
      echo "<tr><td>", $a['id'], "</td><td>", $a['no'], "</td><td>",$a['date'], "</td><td>",$a['mail'], "</td><td>",$a['subject'], "</td><td>", mb_strimwidth($a['comment'], 0, 20, "…"), "</td><td>",mb_strimwidth($a['url'], 0, 20, "…"), "</td><td>",mb_strimwidth($a['host'], 0, 20, "…"), "</td><td>",$a['exid'], "</td><td>",$a['pwd'], "</td><td>",$a['ext'], "</td><td>",$a['img_w'], "</td><td>",$a['img_h'], "</td><td>",$a['filename'], "</td><td>",$a['thumbnail'], "</td><td>",$a['cdt'], "</td><td>",$a['time'], "</td><td>",$a['fontcolor'], "</td><td>", htmlspecialchars($a['tree']), "</td><td>",$a['parent'], "</td><td>",$a['invz'], "</td></tr>\n";
    }
//テーブルおわり
echo "</table>\n";

//db切断
   $db = null;
  } catch (PDOException $e) {
    echo "<p>エラー:", $e->getMessage(), "</p>";
  }

echo("作業おわり");
exit;

ログデータに削除記事があった場合、primary idと記事NOが合わなくなるので一応並列記憶
回避ルーチンを入れてるので確認できたらnoの方は消す


なんかプリペアドステートメントが美しくないw
適当すぎるw