2022年7月1日 星期五

跟著書做~~PHP網頁程式設計 超入門

 這本書真的符合我的需求,說明清楚,每個步驟都講解原理,讓我受益良多,值得一讀。

安裝了XAMPP大概十幾天,原以為XAMPP是本機運行,外面應該進不來,就沒有設定MYSQL的密碼,某天搜尋PHP的相關資訊時(FSecure都顯示綠燈,但有一個網站是有擋住),突然程式一直顯示無法連結資料庫。原以為是程式出問題,晚上回來時,看著Control Panel,MYSQL啟動不了,Apache的port一直在更改,感覺好像是portscan,當下趕快斷網,還原系統後,解安裝XAMPP並刪除資料夾,重新安裝一次。

這次有影響到還原系統的時候,原本WIN11商家關閉outlook.com登入,這次關不了,所以只好新增一個帳號@@
chrome的F-Secure毀損(這次FSecure都沒響???)。
VS code竟也開不了,有點擔心是硬碟的問題嗎???VS code再跑一次安裝程式(沒有解安裝),所以之前安裝桃熊推薦的都還在。現在改用UBUNTU架站(又是一個坑)。

最後還是選擇XAMPP隨身版,感謝KJIE NOTE分享。資料夾不可有中文,第一次要set_xampp.bat,更改MYSQL的ROOT登入方法:HTTP,並更改密碼。

  1. 安裝XAMPP,加上visual code,搭配"桃熊"推薦的工具。(PHP IntelliSense、HTML Snippets、Beautify)其他的尚未測試。

  2. 單引號' ' 是處理字串,雙引號" " 是可以處理變數。
    以前都是直接使用雙引號搭配單引號來echo,但這次有遇到錯誤,反而使用書上的單引號+逗號,就沒有問題,例如 P.4-47
    <select name="code">
    $store=['新宿'=>100, '名古屋'=>200];    
    foreach($store as $key=>$value){    // key=新宿,value=100 
    echo '<option value="', $value, '">', $key, '</option>';
    }
    上述的echo,我以前會直接寫成 echo "<option value='$value'>$key</option>"; 這邊沒問題。
    但接收頁就出問題了,如下
    正確:echo '分店編號為', $_REQUEST['code'];
    錯誤:echo "分店編號為 $_REQUEST['code']";

  3. preg_match可以用來檢查格式。if(preg_match('/^[0-9]{7}$/', $postcode))
     一開始是用/來包住檢查,檢查$postcode是否有七個0~9數字。
    ^:句首
    $:句尾
    [0-9]:0~9的數字一個
    {7}:符合前項格式七個

    if(preg_match('/^[0-9]{3}-[0-9]{4}$/', $postcode)),檢查$postcode是否符合123-3456的格式。

    if(preg_match('/(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9]{8,}/', $password)),檢查密碼要包含小寫字母、大寫字母、數字至少各一個,總共要八個字以上。
    「.」代表任意1字。「*」代表前面的文字重複0次以上,所以「.*」表示文字重複0次以上。
    (?=.*[a-z]):包含小寫英文字母
    [a-zA-Z0-9]:小寫、大寫字母、數字各一個
    {8,}符合前項格式的文字8個以上

    if(preg_match('/[0-9]+/', $postcode))
    有一個以上(+號) 0~9的數字。

  4. mb_convert_kana(字串,類型代碼),代碼解說如下
    A:半形轉全形。a:全形轉半形。

  5. 檔案上傳,使用enctype
    <form action="upload-output.php" method="post" enctype="multipart/form-data">

  6. 在寫入資料庫的頁面insert-output.php使用htmlspecialchars函式,可以將特殊意義的標籤失去作用。所以一般還有用javascript在填寫表格的時候就先檢查。

  7. 資料隱形碼攻擊(SQL injection),可以試著用問號?代替變數,並利用prepare及execute方法就可以避免。
    prepare(select * from product where name like ?); 或
    prepare(insert into product value(null, ?, ?));
    execute(htmlspecialchars(變數值)

  8. 結帳與查詢訂單歷史紀錄沒有寫出寫程式的思路,但有提供檔案。
    purchase-input:結帳的畫面,帶出姓名與地址及購物車裡的東西。
    purchase-output:列出最後的一筆訂單,結帳時,insert到 purchase及 purchase_detail中。
    history:查詢購買紀錄。

自練時間
  1. A.php 用 form action要傳給B.php,傳那些資料呢?
    用input的text 讓使用者輸入資料或是用hidden 自己帶入資料。
    用input的name='user' 來區分有哪些資料。
    最後用 input type="submit" 來送出到B.php。

    B.php使用 htmlsepcialchars($_REQUEST['user'])接收,並搭配htmlspecialchars來讓HTML標籤失效。

  2. 若是接收的REQUEST常用,就可以使用變數如 $user = $_REQUEST['user']
    變數前面一定要有$,開頭必須英文字母(大小寫不同),不可是數字。

  3. 常數是前面沒有$,全部大寫,但要宣告 const如,常數 稅率為0.08,const TAX=0.08;

  4. 陣列跟變數一樣,多了中括號,如 $stock['apple', 'MS']

  5. 做任何判斷寫在B.php上,A.php則可以使用JS來判斷。
    <p><input type="radio" name="meal" value="日式套餐">日式套餐</p>
    <p><input type="radio" name="meal" value="西式套餐">西式套餐</p>
    判斷的時候先抓name的變數,如 _$REQUEST['meal'],再依照value來判斷,如
    switch($_REQUEST['meal']{
      case '日式套餐':
        echo '烤魚、味噌、壽司';
        break;
      case '西式套餐':
        echo '薯餅、漢堡、咖啡';
        break;
    }

  6. echo 的時候,遇到html或是文字都用單引號括住,再加上逗號做區隔。變數就不用單引號,接著也是用逗號做相連。
    echo '<option value="' , $i , '">' , $i , '</option>';

  7. A.php用陣列來放在option並使用 foreach 抓陣列資料給客人選。
    且要注意PHP5.4版之前,必須要用array函式
    $question=array('第一部電影' , '第二滴血' , ...);
    PHP5.4之後就直接用中括號
    $question = ['第一部電影' , '第二滴血'];
    foreach ($question as $item){
     echo '<option value="' , $item , '">' , $item , '</option>';

  8. $$board=json_decode(file_get_contents($file));
    用json_decode取出檔案時,$board是array,可以用 echo gettype($board) 來查詢。
    可是這樣TXT檔變成\uXXXX,\u是utf8內容,XXX是16進位的文字編碼。雖然有逗號區分,但要直接刪除新增有點難度,之前有使用過讀取TXT(感謝kris的電腦筆記的分享)
    使用EXCEL,這樣使用者可能比較好處理。(感謝就是要玩的分享程式前沿地分享)

  9. 檔案上傳是用 input type="file" name="file",但在action 還要加上 enctype="multipart/form-data"。自己有修改一下,改成上傳的時間當檔名(感謝 SANKAI的分享)(感謝IT人分享上傳的漏洞)(感謝IT邦分享PHP安全性)(感謝EZ直接寫出只看副檔名就有危害)

  10. 先建立好資料庫、使用者,規劃好欄位(之後很難增加@@),

  11. 使用PDO類別(class)連線資料庫,必須要用 new,產生instance(配置到電腦記憶體以供使用),如: $dpo = new PDO('mysql:host=localhost; dbname=shop; charset=utf8',
                                                     '使用者帳號', '使用者密碼');
    $sql=$pdo->prepare('select * from product where name=?');
    $sql->execute([ $_REQUEST['keyword'] ]);
    $sql=null;
    $pdo=null;

    使用prepare與execute可以避免SQL injection資料隱碼攻擊。
    但記得以前要關閉sql連線,果然,感謝it145的分享,要將執行SQL的變數 $sql=null,再將PDO變數 $pdo=null。但是瀏覽這個網站後(也許前面幾個網站就有問題,但我有安裝F-Secure,都顯示綠燈沒問題) MYSQL就不能啟動,Apache的port 一直在跳動,很像portscan。更。

  12. 搜尋功能。('select * from 資料表名稱 where name like ?');
    搭配變數 ['%' . $_REQUEST['keyword'] . '%' ]。
    邏輯如下:
    A網頁使用input的name設定好名稱,例如keyword,用action + submit傳到B.php。
    B網頁就new 個PDO,
    叫PDO去prepare準備個SQL語法(這時要用?來當變數),
    接著execute執行剛剛的SQL再帶入傳來的變數放到?裡(變數要用中括號[]加逗號包住),

    最後用php的foreach搭配SQL的fetchAll()將所有搜尋的資料帶出。

  13. 新增資料。('insert into 資料表名稱 values(null, ?, ?, ...));
    搭配變數[ $_REQUEST['name'] , $_REQUEST['price'] ]
    邏輯如下:
    A網頁使用多個input(看要新增幾個資料),用action + submit傳到B.php。
    B網頁就new 個PDO,
    叫PDO去prepare準備個SQL語法(這時要用?來當變數),

    在execute前、後可以下判斷來區分商品是否有輸入(empty),或是價格是否是數字(preg_match),execute輸入的是否有危害程式碼(htmlspecialchars)
    execute執行剛剛的SQL再帶入傳來的變數放到?裡(變數要用中括號[]加逗號包住),

  14. 更新資料。("update 資料表名稱 set name='松果', price=700 where id=1");
    先取出資料放在input中,並帶個hidden的唯一碼,如ID。這裡是直接將所有的商品名稱與價格放在input中,一般可能還是要先點選商品的修改才進入修改畫面。
    邏輯如下:
    A網頁先new 個PDO,並foreach取出所有的資料,這時將每一筆資料都放在form中,並action B網頁,且將資料放在input中,再加上hidden唯一值與submit按鈕。
    B網頁new個PDO,prepare update的SQL,先用if 篩選一下資料,else if 資料正確就execute剛剛的SQL再帶入傳來的變數,並將網頁導向A網頁或是商品頁。

  15. 刪除資料。("delete from 資料表名稱 where id=?");沒有where 會刪除所有的資料喔
    邏輯如下:
    A網頁new 個PDO,並foreach取出所有的資料,加個唯一值的連結到B網頁。(不是action)
    echo '<a href="B.php?id=' , $row['id'] , ' ">確定刪除</a>';
    B網頁 new 個PDO,prepare 刪除資料的SQL(where id =?),if 執行execute帶來的變數正確,就可以將網頁導回A網頁或是商品頁。

  16. 新增、修改、刪除功能按鈕放在A網頁。多功能後直接回到自己頁面。
    新增:在form裡action到B網頁,放一個hidden的input,name="command" value="insert"。
    修改、刪除:先new個PDO,foreach取出所有的資料,這裡注意修改放一個form,刪除是另一個form,form應該是為了submit,所以沒有id名稱,而是個別使用hidden的value來讓B網頁區分,例如修改用value="insert",刪除用value="delete"。但因為修改及刪除原本就要帶一個hidden唯一值(ID),所以要注意。
    B網頁new個PDO(若是放在A網頁中,就不用new PDO) ,先isset($_REQUEST['command']確認是否有command,再用switch($_REQUEST['command'])區分要處理的功能。

  17. 資料庫可以增加很多資料表,且可以將很多資料表互相相連。
    唯一值:unique,用在帳號。
    自動增加:auto_increment,用在商品及客戶的ID。

    id int auto_increment primary key,
    外部鍵值:foreign key(customer_id) references customer(id),意思是customer_id只能放入customer表中已存在的ID。
    複合主鍵:primary key,每一筆資料必須為不可重複的值,通常搭配auto_increment
    但這裡可以兩個ID資料合併成為複合主鍵primary key(purchase_id, product_id)
    例如一筆訂單會有多筆一樣的訂單明細,purchase_id是訂單編號,product_id是商品編號,明細主要是看有多少數量。例如今天一筆全聯訂單,訂了很多商品,有的三個數量,兩個數量不等。

  18. 帳號密碼登入要利用Session機制。在login-output.php那邊先設定session_start();
    這邊先將同SESSION登出,unset( $_SESSION['customer'] );
    再select * from customer 比對帳號與密碼,新給一個$_SESSION['customer'],foreach這個帳號的資訊放入SESSION中,這裡並沒有特別將SESSION做推送,而是PHP處理完後產生SESSION,伺服器會Response給使用者的瀏覽器,這時瀏覽器就儲存。
    登出則是連結到logout-output.php,只要是要處理$_SESSION,都要先sesson_start();再用if(isset($_SESSION['customer'])){  // 還是要先確認是否有 session
        unset($_SESSION['customer']);  // 再刪除 session,
        echo '登出成功';                          // 成功的話就顯示
        }else { echo '您原本已經登出'; } 

  19. 會員新增與修改是利用同一個頁面customer-input.php,用session_start(); 並將 $name=$address=$login=$password=''; 利用isset($_SESSION['customer']); 若有SESSION就可以將$name=$_SESSION['customer']['name']; 再放在forrm 的input中,並action="customer-output.php" 來修改。
    感謝4980Z008分享新增資料後導向其他網頁,header("refresh:5 ; url=https://tw.yahoo.com");

    新增資料比較簡單,用insert的SQL加入到table就好了,但更新就要多檢查。
    而customer-output.php也sessioin_start();並接收傳過來的form,並使用SQL先區分ID!=?,怪怪的,因為ID是auto_increment,(可能其他網站看了同一本書製作出類似的網頁,所以會有相同的$_SESSION['customer'],書上寫檢查登入ID是否重複),這是比較保險的做法,因為這頁還可以修改會員資料,所以要ID與login帳號相同的才能做動作。
    接著區分好有或無ID後,再用empty($sql->fetchAll())來看看是否有SESSION['customer']。
    有SESSION表示要更新資料,更新資料庫完後,還要同步更新SESSION例如
    $_SESSION['customer']=['name' => $_REQUEST['name'] ];
    無SESSION表示要新增資料,使用insert into customer values(null, ?, ?)

  20. 購物車搜尋的功能,先檢查是否有$_REQUEST['keyword'],有的話就用之前教的搜尋功能,沒有的話就顯示全部( prepare那邊不用放變數?,在execute([]);  ),在foreach時,將商品名稱加入連結'<a href="detail.php?id=' , $id , ' ">' , $row['name'] , '</a>';

  21. 顯示商品細節頁,先列出商品的圖片等資訊,並加入一個form,action="cart-insert.php"
    (選擇數量,hidden有id, name, price )放入購物車,再放一個連結(XXX.php?id=' , $row['id'] ) 連到我的最愛。

  22. 放入購物車,cart-insert.php 接收傳來的$id=$_REQUEST['id']; 並在這裡建立$_SESSION['product']; 且判斷之前是否有$_SESSION['product']; 。沒有就建立空的$_SESSION['product']=[];,有就將原本的count取出,再加上傳來的$_REQUEST['count'];
    這頁裡面有包含require cart.php,是顯示購物車每一個ID的內容,並包含著刪除的連結,也有計算個別的ID小計,與總計金額。(找到台部落也是很好奇session跟array到底可以多大)

  23. 刪除購物車的商品,unset($_SESSION['product'][$_REQUEST['id'] ] );刪除傳來的ID

  24. Wish List 我的最愛,favorite-show.php,將$_SESSION['customer']['id'] 及傳來的 $_REQUEST['id'] 存入我的最愛。可是這裡是每一個人每一個商品,就是一筆資料,哇,能否A先生有幾個商品最愛?用array嗎?
    之前的購物車全部都是依靠SESSION,但網購的經驗好像購物車商品沒有因登出後就消失,或許可以寫個購物車資料表。這本書只有寫 客戶資料表、我的最愛資料表、商品資料表、訂單資料表、訂單明細資料表。

  25. 訂單明細,跟我的最愛一樣,每一個商品就是一筆資料,哇。但這裡書上沒有講解,可能有商業機密喔!!因為這裡竟然是取max(id),但若是大的網站,有可能下一秒就有幾個人訂購。就差最後一步了,可惜@@

  26. 隱藏錯誤訊息,程式開發時,務必要讓錯誤訊息顯示,但上線公開時,請將錯誤訊息隱藏。本書範例是在header.php那邊加入<?php error_reporting(0); ?>

  27. 常用的函式庫,時間處理:Carbon,HTTP傳輸:Guzzle,圖檔加工:Imagine,Mail傳送:PHPMailer,圖表繪製:pChart,使用者管理:Sentry,輸入值驗證:Validation

  28. PHP框架,CakePHP、Laravel、Codeigniter、Symfony、Zend Framework

  29. 感謝這本書的作者











沒有留言:

張貼留言

注意:只有此網誌的成員可以留言。