SQLの窓

2019年02月03日


PHP で MySQLをテストする為のソースコード


※ $_GET['text'] で入力された SQL が引き渡されます。
※ GET コマンドなので、内容はアドレスバーで確認する事ができます
※ Form を使わずにアドレスバーから直接 SQL を実行できます。
( IE11 は不可 )

列(セル)の HTML の出力用に関数を作成しています。内部は『ヒアドキュメント』を利用した比較的可読性の高い方法を用いて文字列を作成しています。

ヒアドキュメント構文では、最後の識別子をインデントしてはいけないので関数内でHTML の階層を単純化するほうがメンテナンス性が高くなると思います。

QueryString に text が無い場合と text に有効な文字が全く無い場合は  show variables でシステム変数の一覧を表示します
if ( !isset( $_GET['text'] ) || trim($_GET['text']) == "" ) {
	$_GET['text'] = "show variables";
}
MySQL 改良版拡張モジュール
<?php
session_cache_limiter('nocache');
session_start();

header( "Content-Type: text/html; charset=utf-8" );

?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>SQL実行結果</title> 
<style>
table {
	border: solid 1px #000;
	border-collapse: collapse;
}

th,td {
	border: solid 1px #000;
	padding: 5px;
}
</style>
</head>
<body style='white-space:pre;'>
<?php
if ( !isset( $_GET['text'] ) || trim($_GET['text']) == "" ) {
	$_GET['text'] = "show variables";
}

print $_GET['text'] . "\n";

$server = 'localhost';
$dbname = 'lightbox';
$user = 'root';
$password = 'パスワード';

// ***************************
// 接続
// ***************************
$mysqli = @ new mysqli($server, $user, $password, $dbname);
if ($mysqli->connect_error) {
	print "接続エラーです : ({$mysqli->connect_errno}) ({$mysqli->connect_error})";
	exit();
}

// ***************************
// クライアントの文字セット
// ***************************
$mysqli->set_charset("utf8"); 

// ***************************
// クエリ
// ***************************
$result = $mysqli->query($_GET['text']);
if ( !$result ) {
	print "\n";
	print "<span style='color:#f00'>error : " . $mysqli->error . "</span>";
	exit();
}

// ***************************
// 列数
// ***************************
$nfield = $result->field_count;
if ( $nfield ) {
	$ncount = 0;
	print "<table>\n";

	// 行番号用タイトル
	print "\t<th></th>";

	// 列のタイトルを作成
	$field = $result->fetch_fields( );
	for( $i = 0; $i < $nfield; $i++ ) {

		print_cell_html( "th", $field[$i]->name );

	}

	print "\n";

	// ***************************
	// 行データ
	// ***************************
	while ($row = $result->fetch_row()) {

		print "<tr>\n";
		// 行番号
		print "\t<td>" . ($ncount + 1) . "</td>";

		for( $i = 0; $i < $nfield; $i++ ) {

			print_cell_html( "td", $row[$i] );

		}
		print "\n</tr>\n";

		$ncount++;
	}

	print "</table>";
}

// ***************************
// 接続解除
// ***************************
$mysqli->close();


// ***************************
// セルの HTML 出力
// ***************************
function print_cell_html( $html, $data ) {

print <<<CELL_HTML
<{$html}>{$data}</{$html}>
CELL_HTML;

}

?>

</body>
</html>


PHP 5.5.0 で非推奨になり、PHP 7.0.0 で削除
<?php
session_cache_limiter('nocache');
session_start();

header( "Content-Type: text/html; charset=utf-8" );

?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>SQL実行結果</title> 
</head>
<body style='white-space:pre;'>
<?php
print $_GET['text'] . "\n";

$server = 'localhost';
$dbname = 'lightbox';
$user = 'root';
$password = 'パスワード';

// ***************************
// 接続
// ***************************
$connect = @mysql_connect( $server, $user, $password );
if ( !$connect ) {
	print "接続エラーです";
	exit();
}

// ***************************
// DB選択
// ***************************
mysql_select_db( $dbname, $connect );
mysql_set_charset("utf8", $connect); 

// ***************************
// クエリ
// ***************************
$result = mysql_query($_GET['text'], $connect);
if ( !$result ) {
	print "\n";
	print "<span style='color:#f00'>" . mysql_error() . "</span>";
}

// ***************************
// 列数
// ***************************
$nfield = @mysql_num_fields( $result );
if ( $nfield ) {
	$ncount = 0;
	print "<table style='border:solid 1px #000;border-collapse:collapse;'>\n";

	print "\t<th style='border:solid 1px #000;padding:5px;'></th>\n";
	for( $i = 0; $i < $nfield; $i++ ) {
		$field = mysql_fetch_field( $result, $i );

		print "\t<th style='border:solid 1px #000;padding:5px;'>{$field->name}</th>\n";
	}

	while ($row = mysql_fetch_row($result)) {
		print "<tr>\n";
		print "\t<td style='border:solid 1px #000;padding:5px;'>" . ($ncount + 1) . "</td>\n";
		for( $i = 0; $i < $nfield; $i++ ) {
			print "\t<td style='border:solid 1px #000;padding:5px;'>{$row[$i]}</td>\n";
		}
		print "</tr>\n";
		$ncount++;
	}
	print "</table>";
}

// ***************************
// 接続解除
// ***************************
mysql_close($connect);
?>

</body>
</html>

※ これらのコードは、開発上のテストを目的としているのでセキュリティ上の考慮はされていません。



posted by lightbox at 2019-02-03 18:37 | PHP + データベース | このブログの読者になる | 更新情報をチェックする

2019年02月02日


PHP で ODBC 関数を使って MS Access( データベース )にアクセスして結果を JSON 文字列として出力する



db.php は簡単なクラスですが、MS Access を使用する為に SHIFT_JIS でSQL を作成する必要があります。また、戻された結果も SHIFT_JIS なので UTF-8 に変換しています。利用者は、UTF-8 ベースのソースコードから SHIFT_JIS を意識せずに処理する事が可能になります。
db.php(クラス)
<?php
// ***************************
// データベースクラス
// (このソースは UTF-8N)
//
// UTF-8 => SHIFT_JIS
// ***************************
class DB {
 
	public $connect;
	public $result;
 
// ***************************
// コンストラクタ
// ***************************
	function DB( $connect ) {

		$this->connect = odbc_connect($connect, "", "");

	}
 
// ***************************
// 接続解除
// ***************************
	function close( ) {

		odbc_close( $this->connect );

	}
 
// ***************************
// 単純クエリー( 実行 )
// ***************************
	function query( $sql_query ) {

		// SHIFT_JIS に変換
		$sql_query = mb_convert_encoding( $sql_query, "cp932", "utf-8" );

		$ret = @odbc_exec( $this->connect, $sql_query );
		return $ret;
	}
 
// ***************************
// フェッチ
// ***************************
	function fetch( $result=null ) {

		// 引数が無い場合は前回実行された 
		// $this->result を使用する
		if ( $result == null ) {
			$result = $this->result;
		}

		// ※ Invalid argument supplied for foreach()
		$error = error_reporting(E_ALL & ~E_WARNING);

		// SHIFT_JIS のデータ
		$ret1 = odbc_fetch_array( $result );
		// 空の配列
		$ret2 = array();

		// 数字をインデックスとする配列を作成する
		foreach ($ret1 as $key => $value) {
			// UTF-8 で使えるようにする
			$ret2[] = mb_convert_encoding( $value, "utf-8", "cp932" );
		}
		// 連想配列を作成する
		foreach ($ret1 as $key => $value) {
			// UTF-8 で使えるようにする
			$key = mb_convert_encoding( $key, "utf-8", "cp932" );
			$ret2[$key] = mb_convert_encoding( $value, "utf-8", "cp932" );
		}

		// 関数内での設定以前に戻す
		error_reporting($error);

		// 数字インデックスと文字列インデックスの連想配列
		return $ret2;
	}
 
// ***************************
// クエリーとフェッチ
// ***************************
	function query_ex( $sql_query='' ) {

		// 引数がある場合は、query and fetch
		if ( $sql_query != '' ) {
			$this->result = $this->query( $sql_query );
			if ( !$this->result ) {
				return false;
			}
			return $this->fetch( );
		}
		// 引数が無い場合は fetch のみ
		else {
			return $this->fetch( );
		}
 
	}

// ***************************
// 実行 : query と同じ
// ***************************
	function execute( $sql_exec ) {

		// SHIFT_JIS に変換
		$sql_exec = mb_convert_encoding( $sql_exec, "cp932", "utf-8" );

		$ret = odbc_exec( $this->connect, $sql_exec );
		return $ret;

	}
 
 
}
?>


以下は、accdb を使って( mdb でも可 )データを JSON として出力しています。

ms-access-json.php
<?php
// ***************************
// このソースは UTF-8N
// ***************************
header( "Content-Type: text/plain; Charset=utf-8" );

// ***************************
// キャッシュを使用しない
// ***************************
header( "Expires: Thu, 19 Nov 1981 08:52:00 GMT" );
header( "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0" );
header( "Pragma: no-cache" );

// ***************************
// データベースアクセスクラス
// ***************************
require_once("db.php");

// ***************************
// PHP 設定
// ***************************
mb_language( "ja" );
mb_internal_encoding("UTF-8");

// ***************************
// MS Acceess ファイル
// ***************************
$dbfile = "販売管理B.accdb";
//$dbfile = "販売管理B.mdb";

// ***************************
// ファイル名を SHIFT_JIS に変換
// ***************************
$dbfile = mb_convert_encoding( $dbfile, "cp932", "utf-8" );


// ***************************
// 接続文字列
// ODBC ドライバ名( 32ビット )
// のある場所 ▼
// HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\ODBC\ODBCINST.INI
// ***************************
$connect_string = "Provider=MSDASQL;";
$connect_string .= "Driver={Microsoft Access Driver (*.mdb, *.accdb)};";
$dbpath = realpath($dbfile);
$connect_string .= "dbq={$dbpath};";

// ***************************
// クラスのインスタンス
// ***************************
$db = new DB( $connect_string );
if ( $db->connect === false ) {
	print "接続に失敗しました : " . mb_convert_encoding( odbc_errormsg(), "utf-8", "cp932" );
	exit();
}

// ***************************
// 1) UTF-8 で SQL を作成
// ***************************
$query = <<<SQL
	select
		[社員マスタ].*,
		[社員マスタ].[社員コード] as scode
	from [社員マスタ]
	where [社員コード] in('0001','0002','0003')
SQL;

// ***************************
// データ全体用の配列
// ***************************
$json = array();

// ***************************
// クラスでクエリの実行
// ※ 初回
// ***************************
$column = $db->query_ex( $query );
if ( $column === false ) {
	print "SQLの実行に失敗しました : " . mb_convert_encoding( odbc_errormsg( $db->connect ), "utf-8", "cp932" );
	$db->close();
	exit();
}

// ***************************
// 全ての行を取得
// ***************************
while ( $column ) {

	// ***************************
	// 行単位で結果の連想配列をセット
	// ***************************
	$json['items'][] = $column;
	$column = $db->query_ex( );

};

// ***************************
// 接続解除
// ***************************
$db->close();

// ***************************
// php.exe の確認
// ***************************
if( PHP_INT_SIZE == 4 ) {
	print "PHPは 32ビットです\n";
}

// ***************************
// テストの為、見やすいオプション
// 1) 日本語をエスケープしない
// 2) 整形する
// ***************************
print json_encode( $json, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);

?>


関連する Microsoft ドキュメント( 接続文字列 )

Microsoft OLE DB Provider for ODBC の概要

データ ソースにアクセスする( 接続文字列を作成する )


出力結果
PHPは 32ビットです
{
    "items": [
        {
            "0": "0001",
            "1": "浦岡 友也",
            "2": "ウラオカ トモヤ",
            "3": "0003",
            "4": "0",
            "5": "2005-09-12 00:00:00",
            "6": "2005-11-28 00:00:00",
            "7": "270000",
            "8": "9000",
            "9": "",
            "10": "0001",
            "社員コード": "0001",
            "氏名": "浦岡 友也",
            "フリガナ": "ウラオカ トモヤ",
            "所属": "0003",
            "性別": "0",
            "作成日": "2005-09-12 00:00:00",
            "更新日": "2005-11-28 00:00:00",
            "給与": "270000",
            "手当": "9000",
            "管理者": "",
            "scode": "0001"
        },
        {
            "0": "0002",
            "1": "山村 洋代",
            "2": "ヤマムラ ヒロヨ",
            "3": "0003",
            "4": "1",
            "5": "2005-06-17 00:00:00",
            "6": "2005-09-18 00:00:00",
            "7": "300000",
            "8": "",
            "9": "",
            "10": "0002",
            "社員コード": "0002",
            "氏名": "山村 洋代",
            "フリガナ": "ヤマムラ ヒロヨ",
            "所属": "0003",
            "性別": "1",
            "作成日": "2005-06-17 00:00:00",
            "更新日": "2005-09-18 00:00:00",
            "給与": "300000",
            "手当": "",
            "管理者": "",
            "scode": "0002"
        },
        {
            "0": "0003",
            "1": "多岡 冬行",
            "2": "タオカ フユユキ",
            "3": "0002",
            "4": "0",
            "5": "2005-08-14 00:00:00",
            "6": "2005-11-14 00:00:00",
            "7": "250000",
            "8": "",
            "9": "",
            "10": "0003",
            "社員コード": "0003",
            "氏名": "多岡 冬行",
            "フリガナ": "タオカ フユユキ",
            "所属": "0002",
            "性別": "0",
            "作成日": "2005-08-14 00:00:00",
            "更新日": "2005-11-14 00:00:00",
            "給与": "250000",
            "手当": "",
            "管理者": "",
            "scode": "0003"
        }
    ]
}

32 ビット ODBC の一覧をレジストリエディタで開く

strParam = "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\ODBC\ODBCINST.INI"

' レジストリ書き込み用
Set WshShell = CreateObject( "WScript.Shell" )
' WMI用
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")

' レジストリエディタが最後に開いていたキーの登録を行います
strPath = "Software\Microsoft\Windows\CurrentVersion\Applets\Regedit\LastKey"
if GetOSVersion() >= 6 then
	strRegPath = "コンピューター\" & strParam
else
	strRegPath = "マイ コンピュータ\" & strParam
end if

' 既に regedit が実行中の場合はいったん終了させます
Set colProcessList = objWMIService.ExecQuery _ 
	("Select * from Win32_Process Where Name = 'regedit.exe'") 
For Each objProcess in colProcessList
	' 最後のウインドウの位置とサイズを保存する為の終わらせ方
	WshShell.AppActivate("レジストリ エディタ")
	Wscript.Sleep(500)
	WshShell.SendKeys ("%{F4}")
	Wscript.Sleep(500)
	' 上記終わらせ方が失敗した時の強制終了
	on error resume next
	objProcess.Terminate() 
	on error goto 0
Next 

WshShell.RegWrite "HKCU\" & strPath, strRegPath, "REG_SZ"

' レジストリエディタを起動します
Call WshShell.Run( "regedit.exe" )
' レジストリエディタが終わるまで待つ場合は以下のようにします
' Call WshShell.Run( "regedit.exe", , True )

REM **********************************************************
REM OS バージョンの取得
REM **********************************************************
Function GetOSVersion()

	Dim colTarget,str,aData,I,nTarget

	Set colTarget = objWMIService.ExecQuery( _
		 "select Version from Win32_OperatingSystem" _
	)
	For Each objRow in colTarget
		str = objRow.Version
	Next

	aData = Split( str, "." )
	For I = 0 to Ubound( aData )
		if I > 1 then
			Exit For
		end if
		if I > 0 then
			nTarget = nTarget & "."
		end if
		nTarget = nTarget & aData(I)
	Next

	GetOSVersion = CDbl( nTarget )

End Function




posted by lightbox at 2019-02-02 17:52 | PHP + データベース | このブログの読者になる | 更新情報をチェックする

2018年02月06日


Windows 環境の MySQL + PHP で、ODBC ドライバを使用してデータを取得する場合の3パターン

MySQL ドキュメント : 5.2 Connector/ODBC Connection Parameterscharset パラメータがあります。

SHOW VARIABLES で確認できる 'character_set' 関連が utf8 という環境(character_set_results は空)が前提ですが、charset=cp932 にしてPHP のコードを SHIFT_JIS にすると何も問題無く処理できます。

PHP:shift_jis ODBC:cp932
<?php
header( "Content-Type: text/plain; charset=shift_jis" );

$host = "localhost";$db = "lightbox";$user = "root";$pass="pass";
$connect_string = "Driver={MySQL ODBC 5.3 Unicode Driver};server={$host};database={$db};charset=cp932";
$odbc = odbc_connect($connect_string, $user, $pass);

$query = "select * from 社員マスタ";

if ($result = odbc_exec( $odbc, $query) ) {

	while( $row = odbc_fetch_array( $result ) ) {
		$rows[] = $row;
	}
	odbc_free_result($result);
}

odbc_close( $odbc );

print_r($rows);

?>


しかし、今では一般的な UTF-8(UTF8N) で PHP のコードを使用して、charset を utf8 にしても、戻される行のデータのみが正しく UTF-8 で取得できるだけで、渡す SQL が SHIFT_JIS である必要があるのと、戻る列名も SHIFT_JIS なので2箇所を変換する必要があります。

詳細は良く解りませんが、古い32ビットのソフトウェアで charset を cp932 にして正常に動作している環境でデータをエクスポートすると 列データと insert されたデータのキャラクタセットが違うという結果になります。この事から、日本語用の ODBC ドライバは SHIFT_JIS に特化した処理を行っているのかもしれません。

そもそも MySQL を Windows で使用するにしても、ODBC では無く mysqli を使用すれば PHP サイドからすると事は単純になります。

PHP:utf-8 ODBC:utf8
<?php
header( "Content-Type: text/plain; charset=utf-8" );

$host = "localhost";$db = "lightbox";$user = "root";$pass="pass";
$connect_string = "Driver={MySQL ODBC 5.3 Unicode Driver};server={$host};database={$db};charset=utf8";
$odbc = odbc_connect($connect_string, $user, $pass);

$query = "select * from 社員マスタ";
// SQL は SHIFT_JIS で渡す
$query = mb_convert_encoding($query,"MS932","utf-8");

if ($result = odbc_exec( $odbc, $query) ) {

	while( $row = odbc_fetch_array( $result ) ) {

		$new_row = array();
		foreach( $row as $k => $v ) {
			// 列名が SHIFT_JIS なので変換して使う
			$new_row[mb_convert_encoding($k,"utf-8","MS932")] = $v;
		}
		$row = $new_row;

		$rows[] = $row;

	}
	odbc_free_result($result);
}

odbc_close( $odbc );

print_r($rows);

?>


最後のパターン

charset を cp932 にして PHP を UTF-8 で作成する方法です。UTF-8 の環境の中に無理やり shift_jis の環境をねじ込むようなやり方になっていますが(列もデータも変換する)、Microsoft Access を対象とした ODBC 経由の処理と同じになり、これはこれでいろいろ有効な方法です。

PHP:utf-8 ODBC:cp932
<?php
header( "Content-Type: text/plain; charset=utf-8" );

$host = "localhost";$db = "lightbox";$user = "root";$pass="pass";
$connect_string = "Driver={MySQL ODBC 5.3 Unicode Driver};server={$host};database={$db};charset=cp932";
$odbc = odbc_connect($connect_string, $user, $pass);

$query = "select * from 社員マスタ";
$query = mb_convert_encoding($query,"MS932","utf-8");

if ($result = odbc_exec( $odbc, $query) ) {

	while( $row = odbc_fetch_array( $result ) ) {

		$new_row = array();
		foreach( $row as $k => $v ) {
			$new_row[mb_convert_encoding($k,"utf-8","MS932")] = mb_convert_encoding($v,"utf-8","MS932");
		}
		$row = $new_row;

		$rows[] = $row;

	}
	odbc_free_result($result);
}

odbc_close( $odbc );

print_r($rows);

?>





posted by lightbox at 2018-02-06 23:26 | PHP + データベース | このブログの読者になる | 更新情報をチェックする

2017年06月09日


PHP : SQLインジェクション対策付きの、MySQL のデータを JSON で返す自家製 API テスト用のテンプレート3パターン

特記事項

1) ブラウザの JavaScript からドメイン違いでも呼び出せるように、Access-Control-Allow-Origin を使用
2) pretty=no で、整形しません。escape=no で、日本語はそのまま返します(デバッグ優先)

real_escape_string で SQLインジェクション対策

SQL の実行に query メソッドを使用します。その為、SQL は単純に文字列で作成するので、real_escape_string で条件内の値を $_GET より $get に変換して使用します。
<?php
error_reporting( E_ALL & ~E_NOTICE );

session_cache_limiter('nocache');
session_start();

// 取得対象列
$list = "社員コード,氏名,フリガナ,所属,性別,作成日,更新日,給与,手当,管理者";
// json encode オプション
$flg = 0;

// JavaScript ajax 用
header( "Access-Control-Allow-Origin: *" );
header( "Content-Type: application/json; charset=utf-8" );

// DB 接続
$mysqli = new mysqli('サーバ', 'ユーザ', 'パスワード', 'データベース');
if ( $mysqli->connect_error ) {
	// 接続失敗
	$json = array( "error" => "Connect Error ({$mysqli->connect_errno}) {$mysqli->connect_error}" );
}
else {
	// SQL インジェクション用
	foreach( $_GET as $key => $value ) {
		// $_GET を $get で代替
		$get[$key] = $mysqli->real_escape_string( $value );
	}
	// json encode オプション
	if ( $get["pretty"] != "no" ) {
		$flg = $flg | JSON_PRETTY_PRINT;
	}
	if ( $get["escape"] != "no" ) {
		$flg = $flg | JSON_UNESCAPED_UNICODE;
	}

	$mysqli->set_charset("utf8");

	// ~E_NOTICE なので、単純化
	if ( $_GET["name"] == "" ) {
		$query = "select {$list} from 社員マスタ";
	}
	else {
		$query = "select {$list} from 社員マスタ";
		// 条件オプション
		$query .= " where 氏名 like '%{$get["name"]}%'";
	}

	$result = $mysqli->query($query);
	if ( $result === false ) {
		// SQL 失敗
		$json = array( "error" => "$mysqli->error : {$query}" );
	}
	else {
		// DB 読み出し成功
		$json = $result->fetch_all( MYSQLI_ASSOC );
	}
}

print json_encode( $json, $flg );

?>


bind_param で SQLインジェクション対策

prepare メソッドを使用すると、bind_param メソッドを使用して入力値から SQL を作成する事ができます。そして、結果として get_result メソッド を使用する事によって mysqli_result を取得できるので、その後は パターン(1) と同じになります
<?php
error_reporting( E_ALL & ~E_NOTICE );

session_cache_limiter('nocache');
session_start();

// 取得対象列
$list = "社員コード,氏名,フリガナ,所属,性別,作成日,更新日,給与,手当,管理者";
// json encode オプション
$flg = 0;

// JavaScript ajax 用
header( "Access-Control-Allow-Origin: *" );
header( "Content-Type: application/json; charset=utf-8" );

// DB 接続
$mysqli = new mysqli('サーバ', 'ユーザ', 'パスワード', 'データベース');
if ( $mysqli->connect_error ) {
	// 接続失敗
	$json = array( "error" => "Connect Error ({$mysqli->connect_errno}) {$mysqli->connect_error}" );
}
else {
	// json encode オプション
	if ( $_GET["pretty"] != "no" ) {
		$flg = $flg | JSON_PRETTY_PRINT;
	}
	if ( $_GET["escape"] != "no" ) {
		$flg = $flg | JSON_UNESCAPED_UNICODE;
	}

	$mysqli->set_charset("utf8");

	// ~E_NOTICE なので、単純化
	if ( $_GET["name"] == "" ) {
		$query = "select {$list} from 社員マスタ";
	}
	else {
		$query = "select {$list} from 社員マスタ";
		// 条件オプション
		$query .= " where 氏名 like ?";
	}

	// SQL 準備
	$stmt = $mysqli->prepare($query);
	if ( $stmt === false ) {
		// SQL 失敗
		$json = array( "error1" => "$mysqli->error : {$query}" );
	}
	else {
		// バインドは変数で
		$param = "%{$_GET["name"]}%";

		// パラメータの数が一致しない場合の warning を出さない為の @ 抑制
		@$stmt->bind_param('s', $param );
		if ( false === $stmt->execute() ) {
			$json = array( "error2" => "$mysqli->error" );
		}
		else {
			// MySQL ネイティブドライバ限定(PHP 5.3.0 以降)
			$result = $stmt->get_result();
			if ( $result === false ) {
				$json = array( "error3" => "$mysqli->error" );
			}
			else {
				// DB 読み出し成功
				$json = $result->fetch_all( MYSQLI_ASSOC );
			}
		}
	}
}

print json_encode( $json, $flg );

?>


get_result メソッド を使え無い場合の方法

get_result メソッドは、MySQL ネイティブドライバ限定(PHP 5.3.0 以降)なので、代替方法です。$stmt->store_result() => $stmt->bind_result => $stmt->fetch という流れになります。
<?php
error_reporting( E_ALL & ~E_NOTICE );

session_cache_limiter('nocache');
session_start();

// 取得対象列
$list = "社員コード,氏名,フリガナ,所属,性別,作成日,更新日,給与,手当,管理者";
// json encode オプション
$flg = 0;

// JavaScript ajax 用
header( "Access-Control-Allow-Origin: *" );
header( "Content-Type: application/json; charset=utf-8" );

// DB 接続
$mysqli = new mysqli('サーバ', 'ユーザ', 'パスワード', 'データベース');
if ( $mysqli->connect_error ) {
	// 接続失敗
	$json = array( "error" => "Connect Error ({$mysqli->connect_errno}) {$mysqli->connect_error}" );
}
else {
	// json encode オプション
	if ( $_GET["pretty"] != "no" ) {
		$flg = $flg | JSON_PRETTY_PRINT;
	}
	if ( $_GET["escape"] != "no" ) {
		$flg = $flg | JSON_UNESCAPED_UNICODE;
	}

	$mysqli->set_charset("utf8");

	// ~E_NOTICE なので、単純化
	if ( $_GET["name"] == "" ) {
		$query = "select {$list} from 社員マスタ";
	}
	else {
		$query = "select {$list} from 社員マスタ";
		// 条件オプション
		$query .= " where 氏名 like ?";
	}

	// SQL 準備
	$stmt = $mysqli->prepare($query);
	if ( $stmt === false ) {
		// SQL 失敗
		$json = array( "error1" => "$mysqli->error : {$query}" );
	}
	else {
		// バインドは変数で
		$param = "%{$_GET["name"]}%";

		// パラメータの数が一致しない場合の warning を出さない為の @ 抑制
		@$stmt->bind_param('s', $param );
		if ( false === $stmt->execute() ) {
			$json = array( "error2" => "$mysqli->error" );
		}
		else {
			// get_result() を使わない方法
			$stmt->store_result();
			$row_data = new StdClass;
			$stmt->bind_result(
				$row_data->{"社員コード"},
				$row_data->{"氏名"},
				$row_data->{"フリガナ"},
				$row_data->{"所属"},
				$row_data->{"性別"},
				$row_data->{"作成日"},
				$row_data->{"更新日"},
				$row_data->{"給与"},
				$row_data->{"手当"},
				$row_data->{"管理者"}
			);
			$json = array();
			while ($stmt->fetch()) {
				$json[] = $row_data;
			}
		}
	}
}

print json_encode( $json, $flg );

?>




posted by lightbox at 2017-06-09 20:43 | PHP + データベース | このブログの読者になる | 更新情報をチェックする

2016年05月30日


PHP で 自家製 API を作成して jQuery の簡単なコードで更新処理を行うサンプル

以下のような『社員マスタ』を使って、まず PHP のコードを用意します。
create table `社員マスタ` (
	`社員コード` varchar(4)
	,`氏名` varchar(50)
	,`フリガナ` varchar(50)
	,`所属` varchar(4)
	,`性別` int
	,`作成日` datetime
	,`更新日` datetime
	,`給与` int
	,`手当` int
	,`管理者` varchar(4)
	,`生年月日` datetime
	,primary key(`社員コード`)
)


全てのデータが空でも、SQL 的にはエラーは起こりません。但し、エラーが発生した場合は、kj にエラーメッセージをセットし、furi に 更新 SQL をセットします。

dbdata_update_json.php
<?php
header( "Content-Type: application/json; Charset=utf-8" );
header( "pragma: no-cache" );
header( "Expires: Wed, 31 May 2000 14:59:58 GMT" );
header( "Cache-control: no-cache" );
header( "Access-Control-Allow-Origin: *" );

// ***********************************************
// 初期設定
// ***********************************************
if ( $_GET["sdata"] == "" ) {
	$_GET["sdata"] = "{}";
}
$data = json_decode($_GET["sdata"]);
$data->type = "UPDATE";

// ***********************************************
// 利用変数設定
// ***********************************************
$json_type =  JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT;
$db_data_type = MYSQLI_ASSOC;
$_GET["sdata"] = str_replace("'","''",$_GET["sdata"]);
// -----------------------------------------------


// ***********************************************
// 接続情報
// ***********************************************
$server = 'localhost';
$db_name = 'lightbox';
$user = 'root';
$password = 'trustno1';
// -----------------------------------------------


// ***********************************************
// 接続
// ***********************************************
$connect = @ new mysqli($server, $user, $password, $db_name);
if ($connect->connect_error) {
	$data->status = "ERROR";
	$data->kj = "接続エラーです";
	print json_encode($data,JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);
	exit();
}
$connect->set_charset("utf8"); 
// -----------------------------------------------


// ***********************************************
// NULL 項目の対応
// ***********************************************
if ($data->kyuyo == '') {
	$data->kyuyo = "null";
}
if ($data->teate == '') {
	$data->teate = "null";
}
if ($data->seibetu == '') {
	$data->seibetu = "0";
}
if ($data->birth == '') {
	$data->birth = "null";
}
else {
	$data->birth = "'{$data->birth}'";
}
// -----------------------------------------------


// ***********************************************
// 更新 SQL
// ***********************************************
$query = <<< QUERY
update `社員マスタ` set 
	`氏名` = '{$data->kj}'
	,`フリガナ` = '{$data->furi}'
	,`性別` = {$data->seibetu}
	,`給与` = {$data->kyuyo}
	,`手当` = {$data->teate}
	,`所属` = '{$data->syozoku}'
	,`管理者` = '{$data->kanri}'
	,`生年月日` = $data->birth
 where
 `社員コード` = '{$data->scode}'
QUERY;
// -----------------------------------------------


// ***********************************************
// クエリ実行
// ***********************************************
$result = $connect->query($query); 
if ( !$result ) {
	$data->status = "ERROR";
	$data->kj = "クエリに誤りがあります";
	$data->furi = $query;
	print json_encode( $data, $json_type );
	exit();
}
else {
	$data->status = "SUCCESS";
}
// -----------------------------------------------


// ***********************************************
// 接続解除
// ***********************************************
$connect->close();
// -----------------------------------------------


// ***********************************************
// 結果
// ***********************************************
print json_encode($data,JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);

// -----------------------------------------------

?>


PHP で Access-Control-Allow-Origin: * をセットしているので、どこに置いても JavaScript から呼び出し可能です。

以下のような簡単なコードで更新のテストが可能です。
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script>
$(function(){
	var sdata = {};
	$("#build").on("click", function(){
		$(".entry").find("input").each(function(idx){
			sdata[$(this).prop("id")] = $(this).val();
		});
		$("#result").text( JSON.stringify(sdata,null,"    ") );
	});
	$("#send").on("click", function(){
		var sdata = encodeURIComponent( $("#result").text() );
		$.get("http://localhost/log/easy_board/dbdata_update_json.php?sdata="+sdata ,function(data){
			console.dir(data);
		});
	});
});
</script>
<input id="build" type="button" value="JSON 文字列作成">
<input id="send" type="button" value="サーバ送信">
<pre class="entry">
<input id="scode"> scode
<input id="kj"> kj
<input id="furi"> furi
<input id="seibetu"> seibetu
<input id="kyuyo"> kyuyo
<input id="teate"> teate
<input id="syozoku"> syozoku
<input id="kanri"> kanri
<input id="birth"> birth
</pre>
<pre id="result">
</pre>



posted by lightbox at 2016-05-30 21:17 | PHP + データベース | このブログの読者になる | 更新情報をチェックする

2015年08月06日


PHP : PIPES_AS_CONCAT を使用して、CSV 作成処理をサーバの負荷にして高速に WrodPress のデータを Excel で読み込める CSV として保存する処理

まず、set sql_mode = 'PIPES_AS_CONCAT' を同一セッション内で実行しておく事によって、Oracle と同様に文字列連結に || を使用できるようになります。

そこで、CSV としての 文字列の仕様を SELECT 構文上でほぼ完成させておいて、クライアントでは一つの列を取得して、改行コードの処理のみを加えてファイルに出力するようにしています。ファイルの出力も、ここでは file_put_contents を使っている為、効率は良くありません。大量のデータを処理したい場合ならば、ファイルへの出力は一行単位で行うべきです。

この方法は十数年前の Oracle でレスポンスが顕著で、天と地ほどの処理時間の差が出ていました。現在では、クライアントの処理能力も上がったので差は無いかもしれませんが、一つのテクニックとして利用可能だと思います。

出来上がった SQL
select ID||','||post_author||','||'"'||replace(post_date,'"','""')||'"'||','||'"'||replace(post_date_gmt,'"','""')||'"'||','||'"'||replace(post_content,'"','""')||'"'||','||'"'||replace(post_title,'"','""')||'"'||','||'"'||replace(post_excerpt,'"','""')||'"'||','||'"'||replace(post_status,'"','""')||'"'||','||'"'||replace(comment_status,'"','""')||'"'||','||'"'||replace(ping_status,'"','""')||'"'||','||'"'||replace(post_password,'"','""')||'"'||','||'"'||replace(post_name,'"','""')||'"'||','||'"'||replace(to_ping,'"','""')||'"'||','||'"'||replace(pinged,'"','""')||'"'||','||'"'||replace(post_modified,'"','""')||'"'||','||'"'||replace(post_modified_gmt,'"','""')||'"'||','||'"'||replace(post_content_filtered,'"','""')||'"'||','||post_parent||','||'"'||replace(guid,'"','""')||'"'||','||menu_order||','||'"'||replace(post_type,'"','""')||'"'||','||'"'||replace(post_mime_type,'"','""')||'"'||','||comment_count from wp_posts

<?php
// **************************************
// MIME と キャラクタセットを設定
// **************************************
header( "Content-Type: text/html; Charset=utf-8" );

// **************************************
// キャッシュを無効にするヘッダ
// **************************************
header( "pragma: no-cache" );
header( "Expires: Wed, 31 May 2000 14:59:58 GMT" );
header( "Cache-control: no-cache" );

// **************************************
// グローバル変数
// **************************************
$server = 'データベースサーバー';
$db_name = 'データーベース';
$user = 'ユーザー';
$password = 'パスワード';

print "処理開始<br>";
// **************************************
// データベース接続
// **************************************
$connect = @ new mysqli($server, $user, $password, $db_name);
if ($connect->connect_error) {
	die('Connect Error (' . $connect->connect_errno . ') '
	. $connect->connect_error);
}

// **************************************
// クライアントのキャラクタセット
// **************************************
$connect->set_charset("cp932");

// セッション(接続)中有効
$query = "set sql_mode = 'PIPES_AS_CONCAT'";
// クエリ
$result = $connect->query($query); 
if ( !$result ) {
	die('クエリーに誤りがあります : ' . $connect->error );
}

// **************************************
// WordPress の投稿データ
// **************************************
$query = "select * from wp_posts";
// クエリ
$result = $connect->query($query); 
if ( !$result ) {
	die('クエリーに誤りがあります : ' . $connect->error );
}

// **************************************
// 列名とデータ型を取得
// **************************************
$finfo = $result->fetch_fields();

$name = array();
$type = array();
foreach ($finfo as $val) {
	$name[] = $val->name;
	$type[] = $val->type;
}

// **************************************
// フィールド数
// **************************************
$field_count = $connect->field_count;

$csv = "";
for( $i = 0; $i < $field_count; $i++ ) {
	if ( $i != 0 ) {
		$csv .= "||','||";
	}
	// 数値型
	// TIMESTAMP型(7) を使用している場合は変更が必要です
	if ( $type[$i] < 10 || $type[$i] == 246 || $type[$i] == 16 ) {
		$csv .= $name[$i];
	}
	else {
		$csv .= "'\"'||replace({$name[$i]},'\"','\"\"')||'\"'";
	}

}

$query = "select {$csv} from wp_posts";
// クエリ
$result = $connect->query($query); 
if ( !$result ) {
	die('クエリーに誤りがあります : ' . $connect->error );
}

file_put_contents("query.txt", $query);

$file = "";
while ($row = $result->fetch_array(MYSQLI_BOTH)) {

	// データ内の改行コード
	$row[0] = str_replace("\r\n", "\\r\\n", $row[0]);
	$row[0] = str_replace("\r", "\\r", $row[0]);
	$row[0] = str_replace("\n", "\\n", $row[0]);

	// insert としての一行を追加
	$file .= "{$row[0]}\r\n";

}

// **************************************
// ファイルに出力
// **************************************
file_put_contents("easy.csv", $file);

?>

関連する記事



タグ:PHP MySQL WordPress
posted by lightbox at 2015-08-06 20:05 | PHP + データベース | このブログの読者になる | 更新情報をチェックする
Seesaa の各ページの表示について
Seesaa の 記事がたまに全く表示されない場合があります。その場合は、設定> 詳細設定> ブログ設定 で 最新の情報に更新の『実行ボタン』で記事やアーカイブが最新にビルドされます。

Seesaa のページで、アーカイブとタグページは要注意です。タグページはコンテンツが全く無い状態になりますし、アーカイブページも歯抜けページはコンテンツが存在しないのにページが表示されてしまいます。

また、カテゴリページもそういう意味では完全ではありません。『カテゴリID-番号』というフォーマットで表示されるページですが、実際存在するより大きな番号でも表示されてしまいます。

※ インデックスページのみ、実際の記事数を超えたページを指定しても最後のページが表示されるようです

対処としては、このようなヘルプ的な情報を固定でページの最後に表示するようにするといいでしょう。具体的には、メインの記事コンテンツの下に『自由形式』を追加し、アーカイブとカテゴリページでのみ表示するように設定し、コンテンツを用意するといいと思います。


※ エキスパートモードで表示しています

アーカイブとカテゴリページはこのように簡単に設定できますが、タグページは HTML 設定を直接変更して、以下の『タグページでのみ表示される内容』の記述方法で設定する必要があります

<% if:page_name eq 'archive' -%>
アーカイブページでのみ表示される内容
<% /if %>

<% if:page_name eq 'category' -%>
カテゴリページでのみ表示される内容
<% /if %>

<% if:page_name eq 'tag' -%>
タグページでのみ表示される内容
<% /if %>
この記述は、以下の場所で使用します
container 終わり



フリーフォントで簡単ロゴ作成
フリーフォントでボタン素材作成
フリーフォントで吹き出し画像作成
フリーフォントではんこ画像作成
ほぼ自由に利用できるフリーフォント
フリーフォントの書体見本とサンプル
画像を大きく見る為のウインドウを開くボタンの作成

CSS ドロップシャドウの参考デモ
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり