文章出處

最近要用php,好久不用感覺手生。抓起《零基礎學PHP》一書復習了下,順帶學了smarty模板語言,然后到慕課網看了些php中級視頻教程,這里記錄下。

php最基本的文件上傳

不用任何第三方庫,純html+php的文件上傳。想想很簡單,其實從文件傳輸出錯的返回碼也能看出程序寫的怎么樣,是否考慮到了所有情況。

從upload.html提交表單,上傳文件到upload.php頁面

upload.html:

<!DOCTYPE HTML>
<html>
<head>
    <title>文件上傳練習</title>
</head>

<body>
    <h1>上傳文件練習,最基本的形式</h1>
    <form enctype="multipart/form-data" action="upload.php" method="post">
        上傳此文件:<input type="file" name="myfile" />
        <input type="submit" value="提交上傳" />
    </form>
</body>
</html>

upload.php

<?php
$upload_path = $_SERVER['DOCUMENT_ROOT']."/upload/";

//basename():返回路徑中的文件名部分
$dest_file = $upload_path.basename($_FILES['myfile']['name']);

if(move_uploaded_file($_FILES['myfile']['tmp_name'], $dest_file)){
    echo "文件已經上傳至服務器根目錄的webbasic/upload目錄下";
}else{
    echo "文件上傳中發生了一個錯誤,錯誤代碼:".$_FILES['myfile']['error']."<br/><br/>";

    //var_dump($_FILES);
    //測試發現:如果upload路徑不存在,則上傳肯定失敗,但是$_FILES['myfile']['error']返回值為0,看不出錯誤。
    //也就是說,分成“上傳”和“移動”兩部分,$_FILES['myfile']['error']只負責“上傳”,而是否移動成功則需要根據move_uploaded_file()返回值來判斷。

    echo "錯誤分析:";
    $err_num = $_FILES['myfile']['error'];
    //參考:http://php.net/manual/zh/features.file-upload.errors.php
    switch($err_num){
        case 0:
            $err_msg = "服務器上傳文件存放目錄出錯";
            break;
        case 1:
            $err_msg = "上傳的文件超過了 php.ini 中 upload_max_filesize 選項限制的值";
            break;
        case 2:
            $err_msg = "上傳文件的大小超過了 HTML 表單中 MAX_FILE_SIZE 選項指定的值";
            break;
        case 3:
            $err_msg = "文件只有部分被上傳";
            break;
        case 4:
            $err_msg = "沒有文件被上傳";
            break;
        case 6:
            $err_msg = "找不到臨時文件夾";
            break;
        case 7:
            $err_msg = "文件寫入失敗";
            break;
        default:
            $err_msg = "其他錯誤!!";
    }
    echo $err_msg;
}
?>

PHP處理HTTP請求和SESSION管理

HTTP是無狀態協議,服務器無法僅根據HTTP協議來判斷發起請求的用戶們誰是誰,要用SESSION來區分。實際上這里還需要客戶端瀏覽器開啟cookie來配合,cookie先不表。

最簡單的場景:用戶提交用戶名和密碼進行登錄,發送的是POST請求,這個表單放到html頁面就可以;表單提交后希望查看用戶名和密碼的具體值,要求刷新頁面(再次發POST請求)、直接訪問該頁面(在瀏覽器地址欄后面敲回車,GET請求),都能正常顯示用戶名和密碼的具體值。當然,如果是沒有登陸過就來查看這個頁面,也不應當顯示用戶名和密碼,要重定向到登錄頁。

值得注意的一點:php中重定向比較常見,轉發則比較麻煩。轉發用header("Location:http://www.example.com/some.html")

登錄頁代碼:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>練習題3.4.1</title>
</head>
<body>
    <form name="login" action="prac_bowl.php" method="post" onsubmit="return check_name()">
    <table border="0" width="300px" align="center">
        <tr>
            <td>用戶名:</td>
            <td><input type="text" name="user_name"></td>
        </tr>
        <tr>
            <td>密碼:</td>
            <td><input type="password" name="user_pass"></td>
        </tr>
        <tr>
            <td><input type="submit" value="提交"></td>
        </tr>
    </table>
</form>
</body>
</html>

請求處理頁:

<?php

session_start();

if(isset($_SESSION['user'])){
    $user = $_SESSION['user'];
    $pass = $_SESSION['pass'];
}else{
    //第一次發起POST請求,那么檢查POST中相應字段是否不為空
    if(isset($_POST['user_name']) && isset($_POST['user_pass'])){
        //POST相應字段不為空,則取出值并賦值給SESSION存儲
        $user = $_POST['user_name'];
        $pass = $_POST['user_pass'];
        $_SESSION['user'] = $user;
        $_SESSION['pass'] = $pass;  
    }else{
        //POST相應字段有空值,或者不是POST請求,那么重定向到登錄頁面
        header("Location:http://localhost/snowball/prac_login.html");
        exit();
    }
}

echo "用戶名:".$user.'<br/>';
echo "密碼:".$pass.'<br/>';


?>

學習Cookie

概述

HTTP是無狀態協議。如果一個服務器不需要識別不同的用戶,也就不需要維持不同用戶的狀態,就不用SESSION和COOKIE。

服務器為了識別當前請求發起者的身份,通過COOKIE和SESSION一起,進行識別,流程:
服務器會在客戶端首次發起請求時,生成獨一無二的SESSION_ID,并以Cookie的形式返還給用戶,存儲在用戶瀏覽器中。
每次瀏覽器加載網站,瀏覽器都會把cookie發送給服務器,以通知服務器本用戶先前的活動。

/*
Question:Cookie是通過HTTP協議發送的嗎?SESSION是通過HTTP協議發送的嗎?
是和表單請求和請求相應一起發送的嗎?
Guess:應該是放在HTTP報文中的,和表單請求一起、和請求相應一起。
Answer:是一起的。

Cookie的獲取:

通過瀏覽器上抓包發現,HTTP響應中的響應頭包含Set-Cookie字段,值為“PHPSESSID=0e2vloo8fv35qpj448quostco1; path=/”形式

*/

分類

當用戶瀏覽網站時被存儲在臨時內存中,關掉瀏覽器就會被刪除的一種cookie

有確定的過期時間。生命周期內,每次用戶訪問相應網站時持久cookie的信息會被傳送給服務器。
也稱tracking cookie(跟蹤cookie)

Secure cookie(安全cookie)

僅在使用加密連接(比如HTTPS)時被使用。

...

總結起來:
Session cookie(會話cookie)不帶expire時間,那么相應地,HTTP響應中的Set-Cookie字段的值如果不帶"Expires"或者"Max-Age"字樣,那么一定是Session cookie。
/*
開個腦洞:wikipedia上說指定了Expires的cookie,會由瀏覽器在特定時間刪除它。特定時間是什么?應該是Expires指定的deadline吧。那么我如果我的cookie還有24小時才過期,我現在關機,過了兩天后我打開電腦但是不運行瀏覽器,或者干脆把硬盤拆下來接到另一個電腦上去讀取原有的cookie,不行嗎?

或許把這樣的cookie傳送給server已經不行了,但是拿到這原始的cookie后我們可以去修改cookie的過期時間,去hack。

所以我覺得wikipedia上的說法不正確。不能說瀏覽器刪除cookie,而是cookie自動過期,瀏覽器檢測到他們過期了,所以才刪除他們。
額,好糾結,好像繞進去了,反正就是過期的cookie不能用,要用就需要hack。
*/

/
新問題:服務器端怎樣設定發送給客戶端的cookie類型?主要是session cookie和track cookie。
/

頁面靜態化

靜態化就是把動態頁面的內容,輸出到緩沖區(而不是直接給用戶),緩沖區內容再輸出到文件(靜態文件),然后用戶訪問靜態文件。
我認為模板技術就是復雜版的靜態化技術:簡單的靜態化就是把標準輸出進行緩存,然后將緩存寫入文件;模板技術在進行緩沖的同時還包括變量替換、編譯等。兩者本質是相同的。
模板文件一般是html代碼為主,php腳本語句穿插其中。

靜態化的步驟:

ob_start();   //開啟緩沖區
get_variables();  //獲取變量,相關計算等。比如從數據庫獲取查詢結果
require_once('./templates/xxx.tpl');  //引入模板文件。其實是執行了模板文件,只不過輸出被重定向到緩沖區了。
file_put_contents($targetStaticFile, ob_get_clean());   //把緩沖區內容取出并清空,然后把取出的內容寫入到目標靜態文件。  以后訪問目標靜態文件$targetStaticFile就可以查看結果了

一個更加完整的例子:
用戶訪問index.php,如果緩存文件距離上次修改時間在300秒(5分鐘)之內,那么讀取原有的緩存文件index.shtml;否則,重新從數據庫讀取數據并寫入靜態緩存文件index.shtml,并且在頁面上顯示。
index.php內容如下:

<?php


/*
頁面靜態化步驟
1.連接數據庫,然后從數據庫里面獲取數據
2.把獲取到的數據填充到模板文件里面
3.需要把動態的頁面轉化為靜態頁面,生成純靜態化文件
*/

$targetStaticFile = './index.shtml';
//頁面靜態化的第一種方法:利用緩存時間判斷
//在靜態文件上次修改的300秒之內,那么不更新,直接取靜態文件
if(is_file($targetStaticFile) && (time() - filemtime($targetStaticFile)) < 300) {
    require_once($targetStaticFile);
}else{
    //獲取數據庫中的數據
    require_once('./db.php');

    $connect = mysql_connect("localhost","root","");
    mysql_select_db("test", $connect);
    $sql = "select * from news where `category_id`=1 and `status`='open' order by id desc limit 5";
    $result = mysql_query($sql, $connect);

    $news = array();
    while($row = mysql_fetch_array($result)){
        $news[] = $row;
    }

    ob_start(); //開啟output buffering
    require_once('./templates/singwa.tpl');
    file_put_contents($targetStaticFile, ob_get_contents());

    //引入模板文件
    

}

這里使用了ob_get_contents()函數,表示去出緩沖區的內容。ob_get_clean()函數則與之不同,是取出緩沖區內容后要清空緩沖區。這里index.php頁面被調用,顯然用戶是要看到內容的,因此不能把緩沖區內容清空,因而要使用ob_get_contents()而不是ob_get_clean()函數。

緩存

現學現賣,看了imook網上的視頻,自己敲了一下。
所謂緩存其實就是把從數據庫中等地方取出的數據,存儲到磁盤文件(哦,內存緩存?我還沒有學到那里,先說磁盤的緩存好了)。

緩存操作包括:讀取緩存,寫入緩存,刪除緩存。修改緩存就算了,直接刪除后重新寫入好了。
這里緩存文件內容使用了json格式進行存儲,因而使用了json_encode和json_decode來進行編碼解碼。

編寫了兩個php文件:test.php用來調用緩存, response.php是緩存具體操作的實現

test.php:

<?php

require_once('./response.php');

$data = array(
    'id'=>1,
    'name'=>'singwa',
    'type'=>array(4,5,6),
    'test'=>array(1,45,67=>array(123, 'tsysa'),),
);

$file = new File();

//緩存操作,請分別注釋掉其他行來驗證緩存操作是否OK
$result = $file->cacheData('index_mk_cache', $data);  //寫入緩存
//$result = $file->cacheData('index_mk_cache', null);    //刪除緩存
//$result = $file->cacheData('index_mk_cache');      //查詢緩存

echo "<pre>";
if($result){
    var_dump($result);
    echo "success";
}else{
    echo "error";
}

echo "</pre>";

?>

緩存操作的具體實現:

<?php
/*
$arr = array(
    'id'=>1,
    'name'=>'Chris'
    );

$data = "輸出json數據";
$newData = iconv('UTF-8', 'GBK', $data);

echo $newData;
echo json_encode($newData);
*/
//靜態緩存
class File{
    private $_dir;
    const EXT = ".txt";
    public function __construct(){
        $this->_dir = dirname(__FILE__).'/files/';
    }

    public function cacheData($key, $value='', $path=''){
        $filename = $this->_dir.$path.$key.self::EXT;
        
        if($value===''){
            return $this->getCache($filename);//value為空,則獲取緩存
        }else if(is_null($value)){
            return $this->removeCache($filename);
        }else{
            return $this->putCache($filename, $value);
        }
    }


    private function getCache($filename){
        //獲取緩存
        if(!is_file($filename)){
            return FALSE;
        }else{
            //第二個參數為true,表示返回一個array,而不是object
            return json_decode(file_get_contents($filename), true);
        }
    }

    private function removeCache($filename){
        //刪除緩存(文件)
        return unlink($filename);
    }

    private function putCache($filename, $value){
        //寫入緩存
        $dir = dirname($filename);//dirname:返回路徑中目錄的部分
        if(!is_dir($dir)){
            mkdir($dir, 0777);
        }
        //寫入緩存文件的內容,可以是json格式,也可以是序列化的格式
        return file_put_contents($filename, json_encode($value));
    }
}


?>

php與內存緩存redis

腦補

就像使用mysql一樣,需要一個client和一個server。server不管它,client可以是terminal形式,也可以是web頁面形式。
比如說terminal的形式好了。那么使用redis也一樣,也需要一個terminal。我們可以用redis-cli命令進入redis,類似于mysql進入到mysql的交互界面一樣。

redis默認使用6379端口

安裝redis

看你用什么系統了,Fedora下安裝:

yum install redis
systemctl enable redis
systemctl start redis

windows下安裝:
這里下載

redis基本命令使用

redis-cli 進入交互式命令行
set 變量名字 變量值 #設置緩存
get 變量名字 #獲取緩存
setex 變量名字 失效時間 變量值 #設置緩存值和失效時間

del 變量名字 #刪除緩存

如果緩存變量不存在,返回(nil)

php配置redis

要先安裝phpredis擴展。php默認帶了mysql的擴展所以感受不到,其實都是需要擴展的。
當前(2015年9月4日14:40:46)phpredis擴展在windows下只支持

從網上直接下載編譯好的dll文件即可,一定要選擇和php對應的版本。
php_redis-5.5-vc11-ts-x86-00233a.zip http://d-h.st/4A5
php_igbinary-5.5-vc11-ts-x86-c35d48.zip http://d-h.st/QGH

php_redis-5.5-vc11-nts-x86-00233a.zip http://d-h.st/uGS
php_igbinary-5.5-vc11-nts-x86-c35d48.zip http://d-h.st/bei

php_redis-5.5-vc11-ts-x64-00233a.zip http://d-h.st/1tO
php_igbinary-5.5-vc11-ts-x64-c35d48.zip http://d-h.st/rYb

php_redis-5.5-vc11-nts-x64-00233a.zip http://d-h.st/N0d
php_igbinary-5.5-vc11-nts-x64-c35d48.zip http://d-h.st/c1a

下載后將php_igbinary.dll和php_redis.dll放入php的ext目錄下,

然后修改php.ini,加入這兩個擴展,注意順序不要反了。

extension=php_igbinary.dll

extension=php_redis.dll

重新啟動Apache即可。

運行

先開redis-server

在fedora下就是:

sudo systemctl start redis

在windows下通過mintty進入到redis安裝目錄,執行:

redis-server.exe redis.conf

再開redis-client

我的意思是,以終端的形式開啟redis,即執行:

redis-cli

嚴格講,php.ini中加載了redis,那么只要php代碼中定義了一個redis對象并進行了連接,那也是redis的客戶端了。

執行php代碼

在php代碼中執行redis相關的操作。無非就是設置、讀取、刪除緩存,以及設置緩存失效時間。


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()