Lu-yeom / mentor-program-5th-self-learning

0 stars 0 forks source link

week9(2021/6/7~2021/6/13) #9

Open Lu-yeom opened 3 years ago

Lu-yeom commented 3 years ago

日期:110年6月7日(星期一)15:30-17:00、20:30-23:00
今日進度:[BE101] 用 PHP 與 MySQL 學習後端基礎 #環境建置~資料庫基礎


課程筆記:
一、安裝XAMPP:
 (一)至官網下載依照指示安裝完畢後,點擊xampp.control.exe打開介面。
 (二)可將Apache、MySQL打開。
 (三)在htdocs資料夾新建資料夾(名稱自取,可利用Git-bash),建立新檔案a.php,打開並編輯(可用vim或其他文字編輯器)。
 (四)php語法:

<?php
  echo "Hello World!";
?>  

 (五)在XAMPP介面上點選Apache-->config,選取[PHP],瀏覽器會開啟網頁,在網址列輸入剛剛創立的路徑:http://localhost/user/a.php,就會呈現編輯的內容-->Hello World!
 (六)網址路徑即檔案路徑,可在創立的資料夾內做其他操作,但要注意XAMPP預設Apache與php可以對應,如果是其他程式語言或框架,可能會有不同的連結方式。

二、PHP 基礎
 (一)php程式碼基本架構:

  <?php

    ?>
  被包在裡面的內容才可以執行和輸出。php每一行結尾都要加分號 ”;”
 (二)php相關語法:  
功能
php語法 JS語法
變數宣告 $ var
輸出 echo console.log
字串拼接 . (點) +
換行 <br> (html語法) \n

 1.判斷式(if...else...)、迴圈(for)...等等,變數之前都要加上”$”(例:$i)
 2.取得array的長度:

<?php
  $arr = array(1, 2, 3, 4, 5);
  echo "length:" . sizeof($arr) . "<br>";
  echo $arr[sizeof($arr) -1];
?>

 3.印出array:
  (1)方法一~var_dump

<?php
  $arr = array(“agdfhjgj”, 2, 3, 4, “wetk”);
  var_dump($arr);
?>

 可輸出陣列的內容和型態。
  (2)方法二~print_r:不會輸出型態,較簡潔。
 4.函式:

<?php
  function add($a, $b) {
    return $a + $b;
  }
  echo add(1,3);
?>

 (二)Apache 與 PHP 原理簡介
request(test.php) => apache(server) => php => output => apache => response
Apache的功能:

function run(request) {
  response = php(request)
  send response
}

三、資料庫基礎
 (一)資料庫系統簡介

 資料庫系統分為兩種:
 1.關聯式資料庫:類似excel,可設定很多工作表tab,利用工作表彼此間的關聯來存取資料。較常用。有名的有MySQL、PostgresSQL、
 2.NoSQL(Not Only SQL):無關聯資料庫,常用在存檔log,較無結構概念。有名的有MongoDB、MicroSoft SQL。

四、Table 表格基礎+五、MySQL語法基礎
(連不上phpMyAdmin,先跳過)


Lu-yeom commented 3 years ago

日期:110年6月8日(星期二)14:00-16:00、19:30-23:00
今日進度:[BE101] 用 PHP 與 MySQL 學習後端基礎


課程筆記:

一、MySQL 語法基礎

name: age:
再設置一個data.php,讓表單php可以傳資料到後端data.php,取出資料

<?php

if (!empty($_GET['name']) || !empty($_GET['age'])) { echo '資料有缺,請再次填寫
'; } else {} echo "Hello! " .$_GET['name'] . "
"; echo "Your age is " . $_GET['age'] . "
"; } ?>



---- 
Lu-yeom commented 3 years ago

日期:110年6月9日(星期三)14:00-16:00、20:30-23:30
今日進度:[BE101] 用 PHP 與 MySQL 學習後端基礎


課程筆記:

一、從 PHP 連線到 MySQL 資料庫

?>

二、PHP 與 MySQL 的互動:讀取資料    
* 注意:conn.php內容包含帳號密碼,有資料外洩問題,所以建議把conn.php檔放到 .gitignore  
* 如何在php選擇mySQL資料  
  * 先在資料庫建立幾筆id  
  * 在data.php輸入以下程式碼

<?php require_once('conn.php');

$result = $conn->query("select * from users;"); if (!$result) { die($conn->error); }

$row = $result->fetch_assoc(); print_r($row);

if (empty($_GET['name']) || empty($_GET['age'])) { echo '資料有缺,請再次填寫
'; exit(); }

echo "Hello! " .$_GET['name'] . "
"; echo "Your age is " . $_GET['age'] . "
";

?>

  * 執行後會出現以下訊息  
> Array ( [id] => 1 [username] => aaa ) 資料有缺,請再次填寫  
 * 如果想拿到第二筆資料,```$row = $result->fetch_assoc();
  print_r($row);```要執行兩次 

或是用更好的方法  

<?php require_once('conn.php');

$result = $conn->query("select * from users;"); if (!$result) { die($conn->error); }

while ($row = $result->fetch_assoc()) { echo "id:" . $row['id'] . '
'; echo "username:" . $row['username'] . '
'; }

if (empty($_GET['name']) || empty($_GET['age'])) { echo '資料有缺,請再次填寫
'; exit(); }

echo "Hello! " .$_GET['name'] . "
"; echo "Your age is " . $_GET['age'] . "
";

?>

三、PHP 與 MySQL 的互動:新增資料  
* 建立add.php檔案,新增apple的username  

<?php require_once('conn.php');

$result = $conn->query("insert into users( username) values('apple')"); if (!$result) { die($conn->error); }

print_r($result);

?>

php重整後會回傳1,代表新增成功。  
優化法:  

<?php require_once('conn.php');

if (empty($_POST['username'])) { die('請輸入 username'); }

$username = $_POST['username']; $sql = sprintf( "insert into users(username) values('%s')", $username ); $result = $conn->query($sql); if (!$result) { die($conn->error); }

header("Location: index.php"); ?>

新增user

username:


---- 
Lu-yeom commented 3 years ago

日期:110年6月10日(星期四)19:30-23:00
今日進度:[BE101] 用 PHP 與 MySQL 學習後端基礎


課程筆記:

一、PHP 與 MySQL 的互動:刪除資料

<?php
  require_once('conn.php');

  if (empty($_GET['id'])) {
    die('請輸入 id');
  }

  $id = $_GET['id'];
  $sql = sprintf(
    "delete from users where id = %d",
    $id
  );
  echo $sql;
  $result = $conn->query($sql);
  if (!$result) {
    die($conn->error);
  }

  if ($conn->affected_rows >= 1) {
    echo '刪除成功';
  } else {
    echo '查無資料';
  }

  header("Location: index.php");
?> 

二、PHP 與 MySQL 的互動:編輯資料

<?php
  require_once('conn.php');

  if (empty($_POST['id']) || empty($_POST['username'])) {
    die('請輸入 id 與 username');
  }

  $id = $_POST['id'];
  $username = $_POST['username'];
  $sql = sprintf(
    "update users set username='%s' where id=%d",
    $username,
    $id
  );
  echo $sql;
  $result = $conn->query($sql);
  if (!$result) {
    die($conn->error);
  }

  header("Location: index.php");
?>

三、基礎實戰:Job board 職缺報報

Job board 職缺報報

Job Board 職缺報報

前端工程師

前端工程師的描述前端工程師的描述前端工程師的描述前端工程師的描述前端工程師的描述

薪資範圍:面議

更多資訊

前端工程師

前端工程師的描述前端工程師的描述前端工程師的描述前端工程師的描述前端工程師的描述

薪資範圍:面議

更多資訊
* 管理頁面

<!DOCTYPE html>

Job board 職缺報報

Job Board 職缺報報管理後台

新增職缺

前端工程師

前端工程師的描述前端工程師的描述前端工程師的描述前端工程師的描述前端工程師的描述

薪資範圍:面議

編輯職缺 刪除職缺

前端工程師

前端工程師的描述前端工程師的描述前端工程師的描述前端工程師的描述前端工程師的描述

薪資範圍:面議

編輯職缺 刪除職缺
>
* 管理頁面-新增職缺

<!DOCTYPE html>

Job board 職缺報報

Job Board 職缺報報 - 新增職缺

回到管理頁
職缺名稱:
職缺描述:
薪資範圍:
職缺連結:
>

---- 
Lu-yeom commented 3 years ago

日期:110年6月11日~6月14
今日進度:[BE101] 用 PHP 與 MySQL 學習後端基礎


課程筆記:

一、規劃資料結構以及建置資料庫

  • 設定Database
    • table name: comments
    • id, AI
    • nickname, VARCHAR(128)
    • content, TEXT
    • create_at, DATETIME(CURRENT_TIMESTEMP)

二、實作留言板前端頁面

<!DOCTYPE html>

<html>
<head>
  <meta charset="utf-8">

  <title>留言板</title>
  <link rel="stylesheet" href="style.css">

</head>

<body>
  <header class="warning">
    <strong>注意!本站為練習用網站,因教學用途刻意忽略資安的實作,註冊時請勿使用任何真實的帳號或密碼。</strong>
  </header>
  <main class="board">
    <h1 class="board__title">Comments</h1>
    <form class="board__new-comment-form" method="POST" action="handle_add_comment.php">
      <div class="board__nickname">
        <span>暱稱:</span>
        <input type="text" name="nickname" />
      </div>
      <textarea name="content" rows="5"></textarea>
      <input class="board__submit-btn" type="submit" />
    </form>
    <div class="board__hr"></div>
    <section>
      <div class="card">
        <div class="card__avatar"></div>
      <div class="card__body">
        <div class="card__info">
          <span class="card__author"></span>
          <span class="card__time"></span>
        </div>
        <p class="card__content">
        </p>
      </div>
    </section>
  </main>

</body>
</html>

三、串接資料庫顯示留言

<?php
  require_once("conn.php");

  $result = $conn->query("SELECT * FROM comments ORDER by id DESC");
  if (!$result) {
    die('Error:' . $conn->error);
  }
?>

<!DOCTYPE html>

原本跟著Huli影片照打程式碼,但發現"SELECT * FROM comments ORDER by id DESC"這段如果不打成大寫會出錯,所以SQL的相關用語還是都用大寫比較好。

    <section>
      <?php
        while($row = $result->fetch_assoc()) {
      ?>
      <div class="card">
        <div class="card__avatar"></div>
      <div class="card__body">
        <div class="card__info">
          <span class="card__author"><?php echo $row['nickname']; ?></span>
          <span class="card__time"><?php echo $row['create_at']?></span>
        </div>
        <p class="card__content"><?php echo $row['content']; ?>
        </p>
      </div>
      <?php } ?>
    </section>

這邊的串接資料庫語法很重要,將留言暱稱、內容、時間包起來,再呼叫資料庫把資料叫出來。

四、加入新增留言功能

  • handle_add_comment.php

    <?php
    require_once('conn.php');
    
    if (
    empty($_POST['nickname']) ||
    empty($_POST['content'])
    ) {
    header('Location: index.php?errCode=1');
    die('資料不齊全');
    }
    
    $nickname = $_POST['nickname'];
    $content = $_POST['content'];
    
    $sql = sprintf(
    "insert into comments(nickname, content) values('%s', '%s')",
    $nickname,
    $content
    );
    $result = $conn->query($sql);
    if (!$result) {
    die($conn->error);
    }
    header("Location: index.php");
    ?>
    header('Location: index.php?errCode=1');
    die('資料不齊全');

    先由網址列顯示錯誤訊息

  • index.php
    <h1 class="board__title">Comments</h1>
    <?php 
      if (!empty($_GET['errCode'])) {
        $code = $_GET['errCode'];
        $msg = 'Error';
        if ($code === '1') {
          $msg = '資料不齊全';
        }
        echo '<h2 class="error">' . $msg . '</h2>';
      }
    ?>

    在html header的部分做出若輸入不完整會出現"資料不齊全"的訊息功能,但是如果重新整理,錯誤訊息還是會在,之後繼續修正。

五、規劃會員功能與路由

  • register.php 註冊頁面
  • handle_register.php 處理註冊邏輯
  • login.php 登入表單
  • handle_login.php 處理登入邏輯
  • logout.php 登出

六、規劃會員資料結構以及建置 database

  • id
  • nickname
  • username
  • password
  • created_at

七、實作註冊功能

  • 利用index.php製作register.php,修改board部分內容
    <main class="board">
    <div>
    <a class="board__btn" href="index.php">回留言板</a>
    <a class="board__btn" href="login.php">登入</a>
    </div>
    <h1 class="board__title">註冊</h1>
    <?php 
      if (!empty($_GET['errCode'])) {
        $code = $_GET['errCode'];
        $msg = 'Error';
        if ($code === '1') {
          $msg = '資料不齊全';
        } else if ($code === '2') {
          $msg = '帳號已被註冊!';
        }
        echo '<h2 class="error">錯誤:' . $msg . '</h2>';
      }
    ?>
    <form class="board__new-comment-form" method="POST" action="handle_register.php">
      <div class="board__nickname">
        <span>暱稱:</span>
        <input type="text" name="nickname" />
      </div>
      <div class="board__nickname">
        <span>帳號:</span>
        <input type="text" name="username" />
      </div>
      <div class="board__nickname">
        <span>密碼:</span>
        <input type="password" name="password" />
      </div>
      <input class="board__submit-btn" type="submit" />
    </form>

    因資料庫設定username與nickname不能重複,所以新增"帳號已被註冊"錯誤訊息

  • 設定handle_register.php

    <?php
    require_once('conn.php');
    
    if (
    empty($_POST['nickname']) ||
    empty($_POST['username']) ||
    empty($_POST['password']) 
    ) {
    header('Location: register.php?errCode=1');
    die();
    }
    
    $nickname = $_POST['nickname'];
    $username = $_POST['username'];
    $password = $_POST['password'];
    
    $sql = sprintf(
    "insert into users(nickname, username, password) values('%s', '%s', '%s')",
    $nickname,
    $username,
    $password
    );
    $result = $conn->query($sql);
    if (!$result) {
    $code = $conn->errno;
    if ($code === 1062) {
      header('Location: register.php?errCode=2');
    }
    die($conn->error);
    }
    
    header("Location: index.php");
    ?>

    其中要注意$conn->errno的部分,原本用 die($conn->errno) 不會跳出錯誤,因為連接數字會被判定為錯誤碼沒有動作,改成字串即可。

八、實作登入功能

  • 利用register.php製作login.php,修改board部分內容
    <main class="board">
    <div>
    <a class="board__btn" href="index.php">回留言板</a>
    <a class="board__btn" href="register.php">註冊</a>
    </div>
    <h1 class="board__title">登入</h1>
    <?php 
      if (!empty($_GET['errCode'])) {
        $code = $_GET['errCode'];
        $msg = 'Error';
        if ($code === '1') {
          $msg = '資料不齊全';
        } else if ($code === '2') {
          $msg = '帳號或密碼輸入錯誤';
        }
        echo '<h2 class="error">錯誤:' . $msg . '</h2>';
      }
    ?>  
  • 利用handle_register.php設定handle_login.php

    
    <?php
    require_once('conn.php');
    
    if (
    empty($_POST['username']) ||
    empty($_POST['password']) 
    ) {
    header('Location: login.php?errCode=1');
    die();
    }
    
    $username = $_POST['username'];
    $password = $_POST['password'];
    
    $sql = sprintf(
    "SELECT * FROM users WHERE username='%s' and password='%s'",
    $username,
    $password
    );
    $result = $conn->query($sql);
    if (!$result) {
    die($conn->error);
    }
    
    if ($result->num_rows) {
    // 登入成功
    echo '登入成功';
    } else {
    header("Location: login.php?errCode=2");
    }

?>


九、該怎麼記住登入狀態?Cookie 簡介與實作  
* 在handle_login.php新增setcookie功能  

if ($result->num_rows) { // 登入成功 $expire = time() + 3600 24 30; // 30 day setcookie("username", $username, $expire); header("Location: index.php"); } else { header("Location: login.php?errCode=2"); }

設定$expire變數,保留username的cookie從現在時間到30天後  
* 在index.php新增登出功能  
``` * 設定logout.php ``` ``` 在setcookie設定時間-3600,立即過期 * 設定未登入無法發言功能 ```

請登入發布留言

``` 未登入狀態下無提交功能,改為請登入發布留言 十、修正問題:通行證機制簡介與實作 * 設置cookie * 瀏覽器設置token="亂數",發通行證給使用者 * 設定tokens資料庫 * id * token * username * 在utils.php設定亂數的函式 ``` query($sql); $row = $result->fetch_assoc(); $username = $row['username']; $sql = sprintf( "select * from users where username = '%s'", $username ); $result = $conn->query($sql); $row = $result->fetch_assoc(); return $row; // username, id, nickname } ?> ``` * 在index.php先檢查是否取得token ``` query("select * from comments order by id desc"); if (!$result) { die('Error:' . $conn->error); } ?> ``` 十一、PHP 內建 session 機制 * 在handle_login.php加上session.start() ``` query($sql); if (!$result) { die($conn->error); } if ($result->num_rows) { // 登入成功 /* 1.產生 session id (token) 2.把username寫入檔案 3.set-cookie: session-id */ $_SESSION['username'] = $username; $expire = time() + 3600 *24 *30; // 30 day setcookie("token", $token, $expire); header("Location: index.php"); } else { header("Location: login.php?errCode=2"); } ?> ``` 只要利用```$_SESSION['username'] = $username;```就可以產生token並將username寫入檔案 * 在index.php也加上```session_start();``` ``` ``` * utils.php修正 ``` return $s; } function getUserFromUsername($username) { global $conn; ``` * logout.php修正 ``` ``` * handle_add_comment.php修正 ``` 加密=>密文 * 密文=>解密=>明文 * 雜湊 Hash (多對一關係) * 明文=>hash=>文字 十三、修正問題:使用內建 hash 函式 * 修正handle_register.php ``` die(); } $nickname = $_POST['nickname']; $username = $_POST['username']; $password = password_hash($_POST['password'], PASSWORD_DEFAULT); $sql = sprintf( ``` * 修正handle_login.php ``` die($conn->error); } if ($result->num_rows === 0) { header("Location: login.php?errCode=2"); exit(); } // 有查到使用者 $row = $result->fetch_assoc(); if (password_verify($password, $row['password'])) // 登入成功 /* 1.產生 session id (token) 2.把username寫入檔案 3.set-cookie: session-id */ $_SESSION['username'] = $username; ``` * 補強登入功能-註冊後跳至登入頁面(handle_register.php) ``` ``` 十四、修補XSS(跨站執行程式碼)問題:htmlspecialchars * utils.php新增函式escape ``` function escape($str) { return htmlspecialchars($str, ENT_QUOTES); } ``` * index.php留言內容部分呼叫escape函式 ```

``` 保險起見所有輸入都可加上escape函式 ```

``` 十五、修正SQL Injection問題:prepared statement * 修正handle_add_comment.php ``` $content = $_POST['content']; $sql = "insert into comments(nickname, content) values(?, ?)"; $stmt = $conn->prepare($sql); $stmt->bind_param('ss', $nickname, $content); $result = $stmt->execute(); if (!$result) { ``` * 修正handle_login.php ``` $sql = "SELECT * FROM users WHERE username=?"; $stmt = $conn->preprare($sql); $stmt->bind_param("s", $username); $result = $stmt->execute(); if (!$result) { die($conn->error); } $result = $stmt->get_result(); if ($result->num_rows === 0) { header("Location: login.php?errCode=2"); exit(); } ``` * 修正handle_register.php ``` $sql = "insert into users(nickname, username, password) values(?, ?, ?)", $stmt = $conn->prepare($sql); $stmt->$bind_param("sss", $nickname, $username, $password); $result = $stmt->execute(); if (!$result) { ``` * 修正index.php ``` $stmt = $conn->prepare('select * from comments order by id desc'); $result = $stmt->execute(); if (!$result) { die('Error:' . $conn->error); } $result = $stmt->get_result(); ?> ``` ----