[PHP] 08 MySQLアプリの自動操作 php-webdriver

[M1 Mac, Big Sur 11.7.2, PHP 8.2.1, MySQL 8.0.31]

製作中のWebアプリをphp-webdriverで自動操作しています。

プログレスバーの方はcookieやセッション変数を使っても解決しませんでした。cookieはブラウザをリロードするまで更新しないので今回のような即時反映には向かないです。

PHPからJavaScriptへの変数受け渡しも反映が遅くて使えません。

<?php
require_once ('../../composer/vendor/autoload.php');
use Facebook\WebDriver\Chrome\ChromeDriver;
use Facebook\WebDriver\WebDriverExpectedCondition;
use Facebook\WebDriver\WebDriverBy;

# MySQLサーバ起動判定
exec('mysqladmin ping', $out, $ret);
$out_str = $out[0];
$out_str2 = sprintf("out_str: %s", $out_str);
echo $out_str2;

if(strpos($out[0],'alive') === false){
    exec('mysql.server start');
}else{
    print 'MySQLは起動しています';
    echo "\n";
}

# Webサーバ起動
exec('php -S localhost:8890 -t "/Volumes/DATA_m1/code/PHP/projects/02_mysql_searcher23" > /dev/null &');

# chromedriverのパス設定
$driverPath = realpath("/opt/homebrew/Caskroom/chromedriver/109.0.5414.74/chromedriver");
putenv("webdriver.chrome.driver=" . $driverPath);

# Chromeを起動しphpスクリプトを実行
$driver = ChromeDriver::start();
$driver->get('http://localhost:8890/input.php');

# Chrome表示待機
$driver->wait(10)->until(
    WebDriverExpectedCondition::visibilityOfElementLocated(WebDriverBy::id("form_submit"))
);

sleep(1);
# 自動入力
$driver->findElement(WebDriverBy::id("input1"))->sendKeys("lpw");
sleep(1);
$driver->findElement(WebDriverBy::id("input2"))->sendKeys("Email");
sleep(1);
$driver->findElement(WebDriverBy::id("input3"))->sendKeys("@yahoo.co.jp");
sleep(1);
# 入力ボタン押下
$driver->findElement(WebDriverBy::id("button"))->click();

# search.phpへの遷移待機
$driver->wait(10)->until(
    WebDriverExpectedCondition::visibilityOfElementLocated(WebDriverBy::id("clockTime"))
);

sleep(1);
# チェックボックス押下
$driver->findElement(WebDriverBy::id("checkBox"))->click();
# 検索ボタン押下
$driver->findElement(WebDriverBy::id("button2"))->click();

?>

[JavaScript] 09 cookieの使用

[M1 Mac, Big Sur 11.7.2]

publicな変数の扱い方が全く分からないので、苦肉の策としてcookieに変数を格納し共有することにしました。

PHP、JavaScript、HTMLが混在するスクリプト内、スクリプト間でどうやって変数の受け渡しをしているのでしょうか。この点においてはC++やJavaよりも難易度が相当高いように思います。

プログレスバーについてもcookieを使って何とかできないか探っていきます。

MySQL検索終了時
<html>
<?php
# MySQLにログイン
$conn = new mysqli('localhost', 'root', 'root');
if (!$conn){
	die("MySQL接続に失敗しました");
} else {
	// print 'MySQL接続に成功しました';
}

$dbs_query = $conn->query('show databases;');
$dbs = $dbs_query->fetch_all(MYSQLI_ASSOC);

$dbs_count = count($dbs);

?>
<head>
	<meta charset="utf-8"/>
	<title>MySQL SEARCHER</title>
	<link rel="shortcut icon" href="favicon.ico">
	
</head>
<body>
データベース、カラム名、検索語を入力してください
<form id="form_submit" action="output.php" method="post">
	<table border="1">
		<tr>
			<td>DB</td>
			<td><input id="input1" type="text" name="db"></td>
		</tr>
	</table>
	<tr>
	<?php
	$num = 0;
	foreach ($dbs as $db) {
		if ($num == 0){
			echo "DB : ";
		}
		echo implode($db);
		if ($num != $dbs_count - 1){
			echo ", ";
		}
		$num = $num + 1;
		
	}
	?>
	</tr>
	<table border="1">
		<tr>
			<td>COL</td>
			<td><input id="input2" type="text" name="col"></td>
		</tr>

	</table>
	<table border="1">
		<tr>
			<td><input id="input3" type="text" name="key"></td>
			<td colspan="2" align="center">
			<input id="button" type="submit" value="検索">
			</td>
		</tr>
	</table>
</form>

<progress class="progress" value="0" id="pb">
</progress>
<br>
<br>

<span id="elapsedTime"></span>
<br>

<span id="clockTime"></span>
<script type="text/javascript">
	function set2fig(num) {
		var ret;
		if( num < 10 ) { ret = "0" + num; }
		else { ret = num; }
	return ret;
	}

	function showTime() {
		var now = new Date();
		var nowhour = set2fig(now.getHours());
		var nowminutes = set2fig(now.getMinutes());
		var nowseconds = set2fig(now.getSeconds());
	
		var clockTime = "現在時刻 " + nowhour + ":" + nowminutes + ":" + nowseconds;
		console.log(clockTime);
		document.getElementById("clockTime").innerHTML = clockTime;
    
    	return clockTime;
  	}
	
	setInterval('showTime()', 1000);
</script>
<br>

<span id="loadCount"></span>
<script language="javascript" type="text/javascript">
	var cnt;        // 読込回数

	function getCookieValue(key) {
		const cookies = document.cookie.split(';');
		for (let cookie of cookies) {
			var cookiesArray = cookie.split('='); 
			if (cookiesArray[0].trim() == key.trim()) { 
				return cookiesArray[1];
			}
		}
		return '';
	}

	// cookie使用可否確認
	if (navigator.cookieEnabled){
		console.log("cookie使用可能");
		
		// 読込回数をHTMLに表示
		if (!document.cookie){
			document.getElementById("loadCount").innerHTML = "1回目の読込";
			document.cookie="counts=1;";  
		}
		else{
			try{
				const cnt_ck = getCookieValue("counts");
				// 読み込み回数をインクリメント
				cnt = parseInt(cnt_ck) + 1;
				document.getElementById("loadCount").innerHTML = cnt + "回目の読込";
				
				//cookieに読込回数書き込み
				document.cookie="counts=" + cnt + ";";
			}
			catch(e){
				document.write("読込回数の取得に失敗しました。");
			}
		}
	}
	else{
		document.write("cookieが使用できません。");
		console.log("cookie使用不可");
	}
		
	if (cnt != 2){
		var now = new Date();
		
		function showElapsedTime() {
			var now2 = new Date();
			const eTime = Math.floor((now2 - now)/1000);
			console.log(eTime);

			// cookieのmaxTimeを都度更新する
			let maxTimeStr = "maxTime=" + eTime + ";";
			document.cookie = maxTimeStr;  

			var eTimeShow = "経過時間(秒) " + eTime;
			console.log(eTimeShow);
			document.getElementById("elapsedTime").innerHTML = eTimeShow;

		return eTimeShow;
		}

		setInterval('showElapsedTime()', 1000);

	} else if (cnt = 2){
		let maxTime_ck = getCookieValue("maxTime");
		var eTimeShow = "経過時間(秒) " + maxTime_ck;
		console.log(eTimeShow);

		// 2回目の読込時は1回目の最大値で固定表示
		document.getElementById("elapsedTime").innerHTML = eTimeShow;
	}

</script>
<br>
<br>

</body>
</html>

参考サイト

[PHP] 07 Xdebugの導入 Command Line Debug Client

[M1 Mac, Big Sur 11.7.2, PHP 8.2.1, MySQL 8.0.31]

XdebugをVSCodeやPhpStormで使おうとするとセキュリティ対策のためかスクリプトでMySQLへの自動ログインができません。認証方式をmysql_native_passwordに変えてもダメでした。コンソールからphpコマンドで実行すると自動ログインは可能です。

調べてみるとXdebugはC/C++用デバッガ LLDBのようにターミナルでも使えることが分かりましたので、早速試しています。これで一気に突破口が開けたような気がします。

変数の確認さえできるのであればIDEは基本不要ですね。エディタで使う機能はフォルダ内検索置換と関数定義追跡、スクリプトの色付け位でしょうか。インタプリタやデバッガはエディタからは使わないです。

launch.jsonやsettings.jsonはなるべく触りたくないです。これまでVSCodeの設定で散々時間を浪費したので。OSやハードに近い立ち位置で開発したいです。介在するIDEやツール類は極力排除します。まあ個人開発ですから好き勝手にやってます。

ところで、Pythonにはpdbというデバッガがあるそうです。機会があれば使ってみたいです。

Xdebugのサイト

[PHP] 06 PhpStormの導入

[M1 Mac, Big Sur 11.7.2, PHP 8.2.1, MySQL 8.0.31]

MySQLアプリのプログレスバー実装であえなく詰んでしまいました。PHPのecho & printデバッグ、JavaScriptのvar_dumpデバッグとChromeのconsole.logだけでは変数の内容を十分に追跡できません。

これでまたJavaScriptから撤退すると3度目になってしまいます。今回は腹をくくって長期戦で攻略したいです。

少し前にVisual C++を学習した際はVisual Studioをある程度使い、言語仕様を把握した上で脱IDEを実行しました。PHP & JavaScript学習もこの作戦でいきます。

PHP専用のIDEとして有名なPhpStormを取りあえずインストールしました。

明日以降、少しずつ学んでいきます。

[JavaScript] 08 デジタル時計配置

[M1 Mac, Big Sur 11.7.2]

MySQLアプリにプログレスバーを実装するのに手間取っていて、解決への取っ掛かりとしてHTMLの正常動作を確認するためデジタル時計を配置しました。

setInterval関数の第1引数に繰り返し実行する関数を入れるのですが、なぜかシングルクオートで囲む仕様になっています。

インタプリタの観点からは関数が格納されているメモリアドレスにアクセスするという意味合いなのでしょうが、正直ユーザーフレンドリーではないですね。

#MySQLアプリ #PHP

<span id="clockTime"></span>
<script type="text/javascript">
	function set2fig(num) {
		var ret;
		if( num < 10 ) { ret = "0" + num; }
		else { ret = num; }
	return ret;
	}

	function showTime() {
		var now = new Date();
		var nowhour = set2fig(now.getHours());
		var nowminutes = set2fig(now.getMinutes());
		var nowseconds = set2fig(now.getSeconds());
	
		var clockTime = "現在時刻 " + nowhour + ":" + nowminutes + ":" + nowseconds;
		console.log(clockTime);
		document.getElementById("clockTime").innerHTML = clockTime;
    
    return clockTime;
  	}
	
	setInterval('showTime()', 1000);
</script>

参考サイト

[Python] 347 MySQLアプリ キャッシュ消去

[M1 Mac, Big Sur 11.7.2, Python 3.10.4, MySQL 8.0.31]

MySQLアプリのキャッシュを消去するスクリプトを書きました。

アプリ起動の都度キャッシュを消すようにすれば、デバッグもはかどります。

いつの間にかMySQLアプリの製作が、Python, PHP, JavaScriptの3言語を使った総力戦と化しています。

#コンソールコマンド実行

import subprocess

proc = subprocess.run("lsof -i:8890", shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
result = proc.stdout.decode('UTF-8')

if "PID" in result :
    result_list = result.split(" ")
    
    # リストの空白データをトリミング
    result_list2 = [ele for ele in result_list if ele != '']

    # リスト10番目の要素がプロセスID
    id = result_list2[9]
    print(f'id : {id}')
    
    # プロセスIDをキャンセル
    cmd = f'kill {id}'
    proc2 = subprocess.run(cmd, shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    
else:
    print('キャッシュはありません')

[Python] 346 MySQLアプリ クエリ中断

[M1 Mac, Big Sur 11.7.2, Python 3.10.4, MySQL 8.0.31]

実行中のクエリを中断するスクリプトを書きました。

Webアプリのボタンを押して中断させたかったのですが、今のところ私のスキルではできないので、このファイルを実行するシェルスクリプトを作成し、とりあえず完了としました。

シェルスクリプトをダブルクリックするとクエリを中断できます。

中断時のChrome
python stop_query.py
import MySQLdb

# MySQLに接続
conn = MySQLdb.connect(user='root')
cur = conn.cursor()

# プロセス確認
proc = f'show processlist;'
cur.execute(proc)
procs = cur.fetchall()

# 実行中クエリを中断
for row in procs:
    print(row)
    
    if 'executing' in row:
        id = row[0]
        print(f'id : {id}')
        kill = f'kill {id};'
        cur.execute(kill)
        
conn.close()

[PHP] 05 MySQLアプリ作成 全テーブル検索

[M1 Mac, Big Sur 11.7.2, PHP 8.2.1, MySQL 8.0.31]

データベース、カラム名、検索語を入力して全テーブル検索できるようにしました。

Pythonとは勝手が違うため、半日以上悪戦苦闘でした。

スクリプトの構成は以下の様になります。
1) mysql_main.php (前回記事と内容は同じ)
2) input.php
3) output.php

PHPはPythonのように配列を丸ごと出力することができないようです。Pythonの使いやすさを再認識しました。

これから実装したい機能は以下の通りです。
1)検索中断ボタン
2)プログレスバー(検索進行度表示)
3)検索結果CSVファイル化
4)検索時間測定

<html>
<?php
header('Cache-Control: no-store, no-cache, must-revalidate');


# MySQLにログイン
$conn = new mysqli('localhost', 'root');
if (!$conn){
	die("MySQL接続に失敗しました");
} else {
	// print 'MySQL接続に成功しました';
}

$dbs_query = $conn->query('show databases;');
$dbs = $dbs_query->fetch_all(MYSQLI_ASSOC);

$dbs_count = count($dbs);

?>
<head>
	<title>MySQL LPW</title>
</head>
<body>
データベース、カラム、検索語を入力してください
<form action="output.php" method="post">
	<table border="1">
		<tr>
			<td>DB</td>
			<td><input type="text" name="db"></td>
		</tr>
	</table>
	<tr>
	<?php
	$num = 0;
	foreach ($dbs as $db) {
		if ($num == 0){
			echo "DB : ";
		}
		echo implode($db);
		if ($num != $dbs_count - 1){
			echo ", ";
		}
		$num = $num + 1;
		
	}
	?>
	</tr>
	<table border="1">
		<tr>
			<td>COL</td>
			<td><input type="text" name="col"></td>
		</tr>

	</table>
	<table border="1">
		<tr>
			<td><input type="text" name="key"></td>
			<td colspan="2" align="center">
			<input type="submit" value="検索">
			</td>
		</tr>
	</table>
</form>
</body>
</html>
<html>
<?php
header('Cache-Control: no-store, no-cache, must-revalidate');
?>
<head>
    <title>MySQL LPW</title>
</head>
<body>
    <?php
    include_once'input.php';

    $db = $_POST['db'];
    printf("DB : %s\n", $db);
    $col = $_POST['col'];
    printf("COL : %s\n", $col);
    $key = $_POST['key'];
    printf("検索語 : %s\n", $key);

    # データベース選択
    $select_db = sprintf("use %s", $db);
    $select = $conn->query($select_db);

    # 全テーブル取得
    $tbls_query = $conn->query('show tables;');
    $tbls = $tbls_query->fetch_all(MYSQLI_ASSOC);

    $counts = 0;
    foreach ($tbls as $tbl) {
        $query = sprintf("SELECT * FROM `%s` WHERE `%s` LIKE '%%%s%%'", implode($tbl), $col, $key);
        $result = $conn->query($query);
        $rows = $result->fetch_all(MYSQLI_ASSOC);
        $count = count($rows);
        $counts = $counts + $count;
    }

    $conn->close();
    exec('mysql.server stop');

    ?>
    <table border="2">
    <tbody>
        <tr>
            <td>件数</td>
            <td width="180"><?php echo $counts; ?></td>
        </tr>
        </tbody>
    </table>
</body>
</html>

[PHP] 04 MySQLアプリ作成

[M1 Mac, Big Sur 11.7.2, PHP 8.2.1, MySQL 8.0.31]

簡単なMySQLアプリを作成しました。

フローは以下の通りです。
1) シェルスクリプトファイルをダブルクリックし、mysql_main.phpを実行する。
2) input.phpが表示される。
3) 検索語を入力し、検索ボタンを押す。
4) output.phpにヒット数が表示される。

作成したスクリプトを土台に肉付けしていきます。

<?php
require_once ('../../composer/vendor/autoload.php');
use Facebook\WebDriver\Chrome\ChromeDriver;

# MySQLサーバ起動判定
exec('mysqladmin ping', $out, $ret);
echo $out[0];
echo "\n";

if(strpos($out[0],'alive') === false){
    exec('mysql.server start');
}else{
    print 'MySQLは起動しています';
    echo "\n";
}

# Webサーバ起動
exec('php -S localhost:8890 -t "/code/PHP/projects/02_mysql_lpw03" > /dev/null &');

# chromedriverのパス設定
$driverPath = realpath("/opt/homebrew/Caskroom/chromedriver/107.0.5304.62/chromedriver");
putenv("webdriver.chrome.driver=" . $driverPath);

# Chromeを起動しphpスクリプトを実行
$driver = ChromeDriver::start();
$driver->get('http://localhost:8890/input.php');

?>
<html>
<head>
	<title>MySQL LPW</title>
</head>
<body>
検索語を入力してください
	<form action="output.php" method="post">
		<table border="1">
		<tr>
			<td><input type="text" name="key"></td>
			<td colspan="2" align="center">
			<input type="submit" value="検索">
			</td>
		</tr>
		</table>
	</form>
</body>
<?php
?>
</html>
<html>
<head>
    <title>MySQL LPW</title>
</head>
<body>
    <?php
    $key = $_POST['key'];
    printf("検索語 : %s\n", $key);

    # MySQLにログイン
    $conn = new mysqli('localhost', 'root');
    if (!$conn){
        die("MySQL接続に失敗しました");
    } else {
        // print 'MySQL接続に成功しました';
    }

    # データベース選択
    $select_db = 'use lpw';
    $select = $conn->query($select_db);

    // クエリの実行
    $query = sprintf("SELECT * FROM `lpw_aa` WHERE `Email` LIKE '%%%s%%'", $key);
    // echo $query;

    $result = $conn->query($query);
    $rows = $result->fetch_all(MYSQLI_ASSOC);
    $count = count($rows);
    // printf("件数 : %d\n", $count);

    foreach ($rows as $row) {
        // printf("%s %s\n", $row["Email"], $row["Name"]);
    }

    $conn->close();
    exec('mysql.server stop');

    ?>
    <table border="2">
    <tbody>
        <tr>
            <td>件数</td>
            <td width="180"><?php echo $count; ?></td>
        </tr>
    </tbody>
    </table>
</body>
</html>

[Python] 345 MySQL自動検索 全テーブル対象 結果CSV化

[M1 Mac, Big Sur 11.7.2, Python 3.10.4, MySQL 8.0.31]

MySQLで自動検索し、ヒットした行の全データをcsvファイルにまとめるスクリプトを書きました。

文字列にリストとしての機能を付与するeval関数を思い出すのに少々時間がかかりました。

久しぶりにリスト内包表記を使いました。相変わらずの優れものです。

import time,datetime,subprocess,os,csv,glob
import MySQLdb

# 今日の日付
today = datetime.date.today()
today_str = str(today)[2:].replace('-','')
print(today_str)

# MySQLの起動確認
proc = subprocess.run("mysqladmin ping", shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
ping_result = proc.stdout.decode('UTF-8')
print(ping_result)

detect = "alive" in ping_result
if detect == False:
    os.system('mysql.server start')
else:
    print("MySQLは起動しています")

# MySQLに接続
conn = MySQLdb.connect(user='root')
cur = conn.cursor()

# 処理時間測定開始
start = time.time()

# データベースdataを選択
select_db = f"use data"
cur.execute(select_db)

# 全テーブルを取得
search_tables = f"show tables"
cur.execute(search_tables)
tables = cur.fetchall()
# print(f"tables : {tables}")

# 検索語と結果ファイル名の設定(同日同検索語の結果ファイルがあれば付番する)
key = 'suzuki'
csv_key = f"{today_str}_{key}"
csv_name = f"/code/Python/MySQL/csv/{today_str}_{key}.csv"
csvfiles = glob.glob(f'/code/Python/MySQL/csv/*.csv')

# リスト内包表記で同日同検索語の結果ファイル数をカウントする
csv_count = sum(csv_key in file for file in csvfiles)
if csv_count > 0:
    print(f'csv_count :{csv_count}')
    csv_num = csv_count + 1
    csv_name = f"/code/Python/MySQL/csv/{today_str}_{key}_{csv_num}.csv"

# 検索
hit = 0
num = 0
for table in tables:
    table2 = str(table).replace('(','').replace(')','').replace(",",'').replace("'",'`')
    print(f"table : {table2}")
    
    search = f"SELECT * FROM {table2} WHERE `Name` LIKE '%{key}%'"
    cur.execute(search)
    rows = cur.fetchall()

    for row in rows:
        print(row)
        row_str = str(row).replace('(','[').replace(')',']')
        print(row_str)
        with open(csv_name, 'a') as f:
            writer = csv.writer(f)
            writer.writerow(eval(row_str))
        hit = hit + 1
 
    # 処理時間算出(秒)
    process_time = time.time() - start
    td = datetime.timedelta(seconds = process_time).total_seconds()

    # 小数点第2位まで表示
    td_2f = f'{td:.2f}'
    print(td_2f)
    
    num = num + 1
    
    # テスト用(1テーブルのみ検索)
    # if num == 1:
    #     break
    
print(f'HIT数 :{hit}')

conn.close()
os.system('mysql.server stop')