デモなのでイロイロ手抜きをしています。
ザックリと言うと、サーバー側の全データを持って来ています。
効率は悪いですが、プログラムとしては見通しが良くなるメリットが有ります(つまり読みやすい)。
レスポンスの遅延を想定して、タイマー処理はsetInterval()では無く、setTimeout()にする、(レスポンスの遅延を想定して)レスポンスが来てからsetTimeout()を設定する。
つまりsetTimeout()を設定する場所はどこでも良いと言う訳では無い。
サーバー・サイドの場合、多数のユーザーが同時にアクセスした場合を考慮する必要があります。
具体的にはサーバー・サイドの場合は、ファイル出力時にファイルをロックしないとデータ破壊される危険性があります。
また ファイルの書き換え(ファイル入力してファイル出力する)は、ファイル入出力を一体としてロックしないとデータ破壊される危険性があります、つまり ファイル入力とファイル出力でファイル・ロックを2つに分けるとファイル・データが破壊される危険性があります。
下記デモでもファイル入力とファイル出力でファイル・ロックは1つだけだと言うことに注意して下さい。
なお (データの削除などで)ファイル・サイズが前より小さくなる場合は「rewind(~)、ftruncate(~,0)」で一旦ファイル・サイズをゼロにする必要があります。
【index.php】
<?php
$fnHTMLSpecialcChars = "htmlspecialchars";
$fnHTMLEntities = "htmlentities";
$fnRawURLEncode = "rawurlencode";
$fnCount = "count";
$fnPrintOut = "PrintOut";
$fnFile_Article = "File_Article";
// Debug用 Switch : {1, 2}
$nDebugSW = 0;
$sEncode = "UTF-8";
$sASC_LF = "\x0A"; // Line Feed.
$sASC_CR = "\x0D"; // Carriage Return.
$sIndention = $sASC_CR;
$sDelimiter = "<>"; // "\t"; //
// $Delimiterは実際は"\t"タブ文字などが推奨されますが、
// デバッグ用表示には"<>"の方が分かりやすいでしょう。
$nJS_Timeout = 500;
$sCommMode_Default = "AJAX";
$sCommMode_ArticleAdd = "ArticleAdd";
$Article_Add = false; // true; //
$nArticleMax = 100;
$nSerialSurp = 0x10000000;
/*
// Debug用
$nArticleMax = 3;
$nSerialSurp = 4;
*/
$sUE_Indention = rawurlencode($sIndention);
$sUE_Delimiter = rawurlencode($sDelimiter);
$sFName_Article = "Article.txt";
$sFName_Template = "Template.html";
$sFFirst_Print = "@Print_";
$sFExt_Print = ".txt";
$sClientIP = Get_ClientIP();
$sPT_Name = "";
if (isset($_POST['Name'])) {
$sPT_Name = $_POST['Name'];
// PrintOut("sPT_Name: {$sPT_Name}");
}
$sPIdentifi = $sPT_Name;
$FName_Print = $sFFirst_Print . $sPIdentifi . "@0" . $sFExt_Print;
$FName1_Print = $sFFirst_Print . $sPIdentifi . "@1" . $sFExt_Print;
if ($nDebugSW) {
if (file_exists($FName_Print)) {
copy($FName_Print, $FName1_Print);
}
touch($FName_Print);
($FHW_Print = fopen($FName_Print, "w"))
|| die("die: fopen w: {$FName_Print}.");
flock($FHW_Print, LOCK_EX); // ファイル排他ロック
// LOCK_SH : ファイル共有ロック
// LOCK_EX : ファイル排他ロック
}
$oTimeStamp = new DateTime();
$sTimeStamp = "{$oTimeStamp->format('Y/m/d H:i:s v')}";
PrintOut("sTimeStamp: {$sTimeStamp}");
PrintOut("sPIdentifi: {$sPIdentifi}");
$sPT_SubmitMode = "";
if (isset($_POST['SubmitMode'])) {
$sPT_SubmitMode = $_POST['SubmitMode'];
PrintOut("sPT_SubmitMode: {$sPT_SubmitMode}");
}
$sPT_Name = "";
if (isset($_POST['Name'])) {
$sPT_Name = $_POST['Name'];
PrintOut("sPT_Name: {$sPT_Name}");
}
$sPT_Comment = "";
if (isset($_POST['Comment'])) {
$sPT_Comment = $_POST['Comment'];
PrintOut("sPT_Comment: {$sPT_Comment}");
}
$sPT_LineMarker = "";
if (isset($_POST['LineMarker'])) {
$sPT_LineMarker = $_POST['LineMarker'];
PrintOut("sPT_LineMarker: {$sPT_LineMarker}");
}
PrintOut("sPT_Name: {$sPT_Name}");
PrintOut("sPT_Comment: {$sPT_Comment}");
PrintOut("sPT_SubmitMode: {$sPT_SubmitMode}");
$vArticleAdd = false; // true; //
if (
"" < $sPT_Name && "" < $sPT_Comment
&& $sPT_SubmitMode == $sCommMode_ArticleAdd
) {
PrintOut("if (sPT_Name && sPT_Comment && ~ArticleAdd)");
$vArticleAdd = true; // false; //
PrintOut("vArticleAdd: {$vArticleAdd}");
}
$sResponseMode = "";
if ($sPT_SubmitMode) {
$vResponseMode = $sCommMode_Default;
}
$sResponse = "";
if (!$sPT_SubmitMode) {
$sResponse = File_Template();
} else {
$sResponse = File_Article();
}
if ($FHW_Print) {
// ファイルclose // ファイル・ロックも開放
fclose($FHW_Print)
|| die("die: fclose: {$FName_Print}.");
}
echo $sResponse;
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function PrintOut($msg)
{
global $FHW_Print, $FName_Print, $sPT_SubmitMode;
if ($FHW_Print) {
$msg = $msg ? $msg : "";
fwrite($FHW_Print, $msg . PHP_EOL)
|| die("die: fwrite: {$FName_Print}.");
}
}
function File_Template()
{
global $sFName_Template, $nDebugSW, $sUE_Delimiter, $sUE_Indention,
$sIndention, $nArticleMax, $sCommMode_Default, $sCommMode_ArticleAdd,
$nJS_Timeout, $fnCcount;
// touch($sFName_Template);
($FHR_Template = fopen($sFName_Template, "r"))
|| die("die: fopen r: {$sFName_Template}.");
// flock($FHW_Template, LOCK_EX); // ファイル排他ロック
// LOCK_SH : ファイル共有ロック
// LOCK_EX : ファイル排他ロック
$sTemplate = "";
while ($line = fgets($FHR_Template)) {
$sTemplate .= $line . $sIndention;
}
$sTemplate = str_replace('<\$DebugSW\>', $nDebugSW, $sTemplate);
$sTemplate = str_replace('<\$CommMode_Default\>', $sCommMode_Default, $sTemplate);
$sTemplate = str_replace('<\$CommMode_ArticleAdd\>', $sCommMode_ArticleAdd, $sTemplate);
$sTemplate = str_replace('<\$UE_Indention\>', $sUE_Indention, $sTemplate);
$sTemplate = str_replace('<\$UE_Delimiter\>', $sUE_Delimiter, $sTemplate);
$sTemplate = str_replace('<\$JS_Timeout\>', $nJS_Timeout, $sTemplate);
// ファイルclose // ファイル・ロックも開放
fclose($FHR_Template)
|| die("die: fclose: {$sFName_Template}.");
return $sTemplate;
}
function File_Article()
{
PrintOut("");
PrintOut("function File_Article()");
global $sFName_Article, $sDelimiter, $AJAX,
$sPT_Name, $sPT_Comment, $fnCcount,
$nArticleMax, $sIndention, $nSerialSurp, $oTimeStamp,
$sTimeStamp, $a1sArticle, $vArticleAdd;
touch($sFName_Article);
($FHRP_Article = fopen($sFName_Article, "r+"))
|| die("die: fopen r+: {$sFName_Article}.");
flock($FHRP_Article, LOCK_EX); // ファイル排他ロック
// LOCK_SH : ファイル共有ロック
// LOCK_EX : ファイル排他ロック
$serial = PHP_INT_MIN;
$begin = PHP_INT_MIN;
$line = null;
if (!$a1sArticle) {
$a1sArticle = [];
while ($line = fgets($FHRP_Article)) {
$line = str_replace(PHP_EOL, '', $line);
$a1sArticle[] = $line;
$a1col = explode($sDelimiter, $line);
}
}
if ($a1sArticle) {
$a1col = explode($sDelimiter, $a1sArticle[0]);
$serial = (int)$a1col[0];
}
if ($vArticleAdd) {
PrintOut("if (vArticleAdd)");
PrintOut("sPT_Name: {$sPT_Name}");
PrintOut("sPT_Comment: {$sPT_Comment}");
$line = "{$sTimeStamp}{$sDelimiter}{$sPT_Name}{$sDelimiter}{$sPT_Comment}";
if ($serial < 0) {
$serial = 0;
} else {
$serial++;
$serial = $serial % $nSerialSurp;
}
$line = $serial . $sDelimiter . $line;
array_unshift($a1sArticle, $line);
// $a1sArticle = array_slice($a1sArticle, 0, $nArticleMax);
PrintOut("serial: {$serial}");
PrintOut("line: {$line}");
$cnt = count($a1sArticle);
PrintOut("count(a1sArticle): {$cnt}");
}
$a1sNewArticle = [];
$article = "";
PrintOut("");
PrintOut("for (i = 0; i < count(a1sArticle); i++)");
for ($i = 0; $i < count($a1sArticle); $i++) {
$line = $a1sArticle[$i];
PrintOut("if ($i < nArticleMax)");
PrintOut("if ($i < $nArticleMax)");
if ($i < $nArticleMax) {
PrintOut("if ($i < nArticleMax)");
PrintOut("line : {$line}, ");
$a1sNewArticle[] = $line;
$article .= ($i != 0 ? $sIndention : "") . $line;
$nArticleSize = count($a1sNewArticle);
PrintOut("article : {$article}, ");
} else {
break;
}
}
if ($vArticleAdd) {
$a1sArticle = $a1sNewArticle;
fseek($FHRP_Article, 0);
rewind($FHRP_Article)
|| die("die: fseek: {$sFName_Article}.");
ftruncate($FHRP_Article, 0)
|| die("die: ftruncate: {$sFName_Article}.");
foreach ($a1sArticle as $key => $line) {
PrintOut($line);
fwrite($FHRP_Article, $line . PHP_EOL)
|| die("die: fwrite: {$sFName_Article}.");
}
}
// ファイルclose // ファイル・ロックも開放
fclose($FHRP_Article)
|| die("die: fclose: {$sFName_Article}.");
return $article;
}
function Get_ClientIP()
{
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
// HTTP_CLIENT_IPをチェック
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
// HTTP_X_FORWARDED_FORをチェック
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
// REMOTE_ADDRをチェック
$ip = $_SERVER['REMOTE_ADDR'];
}
return explode(",", $ip)[0];
}
【Template.html】
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
</head>
<style>
p,
section {
border: thin solid #000;
min-height: 1.5em;
}
#idDebug,
#idDebugControl {
display: none;
}
</style>
<body>
<form method="POST" id="idForm" action="">
<input type="text" id="idName" name="Name" value="" placeholder="Name"><br>
<input type="text" id="idComment" name="Comment" value="" placeholder="Comment">
<button type="button" onclick="AJAXComm('<\$CommMode_ArticleAdd\>')">Submit:(Model)</button>
<input type="hidden" id="idLineMarker" name="LineMarker">
<input type="hidden" id="idSubmitMode" name="SubmitMode" value="<\$CommMode_Default\>">
</form>
<main id="idMain">
<div id="idDebugControl">
<button type="button" onclick="AJAXComm();">AJAXComm:(Event)</button><br>
</div>
<section id="idDebug">
</section>
<section id="idMessage">
</section>
<section id="idArticle">
</section>
</main>
</body>
<script>
nDebugSW = Number("<\$DebugSW\>");
sIndention = decodeURI("<\$UE_Indention\>");
sDelimiter = decodeURI("<\$UE_Delimiter\>");
sCommMode_Default = "<\$CommMode_Default\>";
sCommMode_ArticleAdd = "<\$CommMode_ArticleAdd\>";
nTimeout = Number("<\$JS_Timeout\>");
sSubmitModel = "";
LineMarker = -1;
wDebug = document.getElementById("idDebug");
wDebugControl = document.getElementById("idDebugControl");
wMessage = document.getElementById("idMessage");
wForm = document.getElementById("idForm");
wName = document.getElementById("idName");
wComment = document.getElementById("idComment");
wSubmitMode = document.getElementById("idSubmitMode");
wLineMarker = document.getElementById("idLineMarker");
wArticle = document.getElementById("idArticle");
ConsoleOut(`nDebugSW: ${nDebugSW}`);
ConsoleOut(`wForm: ${wForm}`);
ConsoleOut(wForm);
if (2 <= nDebugSW) {
wDebug.style.display = "block";
wDebugControl.style.display = "block";
}
function AJAXComm(submit) {
ConsoleOut();
ConsoleOut("function AJAXComm()");
if (submit) {
sSubmitModel = submit;
}
var sAJAX_Charset = "UTF-8";
var sAJAX_Server = "";
var err = false; // true; //
ConsoleOut(`sSubmitModel: ${sSubmitModel}`);
ConsoleOut(`wSubmitMode.value: ${wSubmitMode.value}`);
if (sSubmitModel) {
ConsoleOut("if (sSubmitModel)");
wMessage.innerText = "";
if (!wName.value) {
err = true; // false; //
wMessage.innerText += "★Nameが入力されていません\n";
}
if (!wComment.value) {
err = true; // false; //
wMessage.innerText += "★Commentが入力されていません\n";
}
if (!err) {
wSubmitMode.value = sSubmitModel;
}
ConsoleOut(`err: ${err}`);
ConsoleOut(`wSubmitMode.value: ${wSubmitMode.value}`);
sSubmitModel = "";
}
var oAJAX = new XMLHttpRequest();
oAJAX.onload = function () {
ConsoleOut(``);
ConsoleOut(`oAJAX.onload = function ()`);
ConsoleOut(`sSubmitModel: ${sSubmitModel}`);
ConsoleOut(`wSubmitMode.value: ${wSubmitMode.value}`);
if (wSubmitMode.value == sCommMode_ArticleAdd) {
wComment.value = "";
wMessage.innerText = "";
}
wSubmitMode.value = sCommMode_Default;
ConsoleOut();
ConsoleOut("oAJAX.onload");
if (this.readyState === 4 && this.status === 200) {
ConsoleOut("this.readyState: " + this.readyState);
ConsoleOut("this.status: " + this.status);
var res = oAJAX.responseText;
wDebug.innerText = res;
if (res) {
var a1sArticle = res.split(sIndention);
var col = null;
//wArticle.innerHTML = "";
var w = null;
while (w = wArticle.firstChild) {
wArticle.removeChild(w);
}
for (var i = a1sArticle.length - 1; 0 <= i; i--) {
var wP = document.createElement('p');
wArticle.prepend(wP);
// appendChild(element);
var line = a1sArticle[i];
wP.innerHTML = KillTag(line);
a1col = line.split(sDelimiter);
}
if (a1col) {
LineMarker = Number(a1col[0]);
}
wLineMarker.value = LineMarker;
ConsoleOut(`LineMarker: ${LineMarker}, `);
}
}
if (nDebugSW < 2) {
setTimeout(AJAXComm(), nTimeout);
}
}
ConsoleOut(`wForm: ${wForm}`);
ConsoleOut(wForm);
oForm = new FormData(wForm);
ConsoleOut(`oForm: ${oForm}, `);
ConsoleOut(oForm);
for (var d of oForm.entries()) {
ConsoleOut(`${d[0]}: ${d[1]}`);
}
// oAJAX.overrideMimeType('text/plain; charset=' + sAJAX_Charset);
oAJAX.open('POST', sAJAX_Server);
// oAJAX.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
oAJAX.send(oForm);
ConsoleOut(`oAJAX.send(oForm)`);
}
if (nDebugSW < 2) {
setTimeout(AJAXComm(), nTimeout);
}
function ConsoleOut(msg) {
if (nDebugSW) {
msg = msg ? msg : "";
console.log(msg);
}
}
function KillTag(txt) {
return txt
.replace(/&/gs, '&')
.replace(/</gs, '<')
.replace(/>/gs, '>')
.replace(/"/gs, '"')
.replace(/'/gs, ''');
}
</script>
</html>
|
|