💡

(おそらく)私が一番まともに作っているMy-ToDoの詳細

2025/01/18に公開1

こんにちは。ねこがくれです。
今日は私が頑張って作っているウェブアプリのMy-ToDoというソフトウェアの詳細や、苦労したところ、学んだところなどを書いていこうと思います。

使用しているものたち

  1. php(バックグラウンド処理全般)
  2. json(データ保管)
  3. css(デザイン)
  4. JS(ほとんどサービスワーカー)
  5. html(当たり前)

見ての通りDBは未使用です。なぜか?まぁ、なんでか私でもあまりわからないです。
今から一つ言えることは絶対DBで管理したほうが良かったな、ということです。

なぜjsonで管理しているか

おそらく(記憶が定かではないのでおそらく)私の主に使っている無料レンタルサーバーのDB作成数が5個なので、もうDBを作成できないとかそんな理由だった気がします。
どちらにせよ、セキュリティの観点からもあまりよろしくないデータの管理の仕方だと思います。(.htaccessなどでアクセス不可にはしています)
あと、開発開始したのが中学1年になってすぐなのでSQLが不得手だった、というのも理由にあると思います。

→今から再度同じものを作るならちゃんとDBを使おうと思います。

phpを書くうえで

まず、下のコードを見てください。(実際にtodoとしての処理をしているところ)

main/index.php
<?php
ini_set('display_errors', 0);
error_reporting(0);
include('../../header.php');
?>


<?php
header("Cache-Control: no-cache, must-revalidate");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");

include('check.php');

// ログインしていない場合、login.php にリダイレクト

session_start();

// ユザネがセッションにセットされていない場合、ログイン画面にリダイレクト
if (!isset($_SESSION['username'])) {
    header("Location: ../../index.php");
    exit();
}

// 現在アクセスしているURLを取得
$current_url = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];

// 指定された部分を取り除く
$base_url = GetPageURL() . "/";
$access_dir = str_replace($base_url, "", $current_url);

// $ACCESS_DIR に格納
$ACCESS_DIR = $access_dir;

if (str_ends_with($ACCESS_DIR, '/index.php')) {
    $ACCESS_DIR = substr($ACCESS_DIR, 0, -10); 
}

try {
    // ユーザーディレクトリを指定してバージョン確認
    version_check($ACCESS_DIR);
} catch (Exception $e) {
    echo 'エラー: ' . $e->getMessage();
}

if(!($_SESSION['dir'] . '/' == $ACCESS_DIR)) {
    die("アクセス権がありません");
}

// HTMLタグの入力を無効にし、文字コードをutf-8にする
function h($v) {
    return htmlspecialchars($v, ENT_QUOTES, 'UTF-8');
}

include('./WEBHOOK/index.php');

// 変数の準備
$FILE = 'todo.txt'; // 保存ファイル名
$ARCHIVE_FILE = 'archive.txt'; // アーカイブ用のファイル

$id = uniqid(); // ユニークなIDを自動生成

// タイムゾーン設定
date_default_timezone_set('Japan');
$date = date('Y年m月d日H時i分'); // 日時(年/月/日/ 時:分)

$text = ''; // 入力テキスト
$tag = ''; // タグ
$comment = ''; // コメント
$commentIndex = ''; // 削除するコメントのインデックス

$DATA = []; // 一回分の投稿の情報を入れる

$BOARD = []; // 全ての投稿の情報を入れる

// $FILEというファイルが存在しているとき
if (file_exists($FILE)) {
    // ファイルを読み込む
    $BOARD = json_decode(file_get_contents($FILE), true); // trueで配列として取得
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 新規タスク追加
    if (!empty($_POST['txt'])) {
        $text = $_POST['txt'];
        $tag = isset($_POST['tag']) ? $_POST['tag'] : ''; // タグを取得
        // 新規データ
        $DATA = [$id, $date, $text, false, [], $tag]; // タグを含める
        $BOARD[] = $DATA;
        file_put_contents($FILE, json_encode($BOARD));
        // archive.txtにも保存
        $archiveData = $DATA; // 同じデータを使う
        $archiveBoard = [];
        if (file_exists($ARCHIVE_FILE)) {
            $archiveBoard = json_decode(file_get_contents($ARCHIVE_FILE), true);
        }
        $archiveBoard[] = $archiveData;
        file_put_contents($ARCHIVE_FILE, json_encode($archiveBoard));
        sendLine("新規タスクが追加されました。", $text); // タスクの内容も一緒に送信
        echo "新規タスクが追加されました";
    }

    if (isset($_POST['del'])) {
        $NEWBOARD = [];
        foreach ($BOARD as $DATA) {
            if ($DATA[0] !== $_POST['del']) {
                $NEWBOARD[] = $DATA;
            }
        }
        file_put_contents($FILE, json_encode($NEWBOARD));
        sendLine("タスクが削除されました。", $DATA[2]); // 削除されたタスクの内容を送信
        echo "タスクが削除されました";
    }

    if (isset($_POST['done'])) {
        foreach ($BOARD as &$DATA) { // $DATAは参照で扱う
            if ($DATA[0] === $_POST['done']) {
                $DATA[3] = !$DATA[3]; // 完了状態を反転させる

                // 完了または未完了のメッセージを送信
                if ($DATA[3]) {
                    sendLine("タスクが完了しました。", $DATA[2]); // 完了メッセージ
                    echo "タスクが完了しました";
                } else {
                    sendLine("タスクが未完了に戻されました。", $DATA[2]); // 未完了に戻されたメッセージ
                    echo "タスクが未完了に戻されました";
                }
            }
        }
        file_put_contents($FILE, json_encode($BOARD));
    }

    // コメントの追加処理
    if (isset($_POST['comment_text']) && isset($_POST['comment_id'])) {
        $comment = $_POST['comment_text'];
        $comment_id = $_POST['comment_id'];
        foreach ($BOARD as &$DATA) {
            if ($DATA[0] === $comment_id) {
                if (!isset($DATA[4])) {
                    $DATA[4] = []; // コメント配列が存在しない場合は空の配列を初期化
                }
                $DATA[4][] = $comment; // コメントを追加
            }
        }
        file_put_contents($FILE, json_encode($BOARD));
    }

    // コメントの削除処理
    if (isset($_POST['comment_delete_id']) && isset($_POST['comment_index'])) {
        $comment_delete_id = $_POST['comment_delete_id'];
        $comment_index = $_POST['comment_index'];
        foreach ($BOARD as &$DATA) {
            if ($DATA[0] === $comment_delete_id) {
                if (isset($DATA[4][$comment_index])) {
                    unset($DATA[4][$comment_index]); // コメントを削除
                    $DATA[4] = array_values($DATA[4]); // インデックスをリセット
                }
            }
        }
        file_put_contents($FILE, json_encode($BOARD));
    }

    // ページをリダイレクトしてリロード
   // header('Location: ' . $_SERVER['SCRIPT_NAME']);
    header('Location: ./');
    exit;
}
?>

このコード、だめなところ多すぎですよね。今から思うとよくこれで動いたなと思います。

このコードのだめなところ

  1. XSS対策をしていない
  2. (これ指摘しちゃうと根本を否定することになりますが)JSONでデータを管理しているところ
  3. CSRF対策がほとんどされていない
  4. Sessionの有効期限や再生成がない
  5. 「読みづらい。滅茶苦茶。」
  6. $FILE などで直接ファイルを埋め込んでいる。
  7. if (isset(...))見づらい!
  8. エラーハンドリングがほとんどない

…とまぁ、自分でもこんなに欠点が見つかるような駄目コードです。
悪いコードの参考にしていただけたら嬉しいです。(ですが、少しずつ改善していっています)

CSSで頑張ったところ

次にCSSを書くうえで大変だったことを書こうと思います。

  1. ヘッダーにinclude()を使ってるのでinclude先のcssが適用されてしまう
  2. そもそもとしてデザインができない

1は、!inportant属性やidを駆使してなんとかしました。
2はまぁ、ChatGPTくんに
「My ToDoというソフトのデザインを考えて文字にしてください」
と適当に投げかけたらすぐに出してくれました。
ほんとにすごいと思います。(おかげでボックスを駆使したおしゃれ?なデザインにできた)

結構頑張ったデザイン

最後に

とても薄くとても参考にできるような内容だとは思えないです、、。すみません。ですが、結構頑張ったソフトなので一度使ってみてくださると嬉しいです!
最後まで読んでくださりありがとうございます。

Discussion

ねこがくれねこがくれ

今から思うとほんとになんでjsonでやったんだろうなという思いでいっぱいです…。DBの数減らせばよかった