最近要用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
學習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=/”形式
*/
分類
Session cookie (會話cookie)
當用戶瀏覽網站時被存儲在臨時內存中,關掉瀏覽器就會被刪除的一種cookie
Persistent cookie (持久cookie)
有確定的過期時間。生命周期內,每次用戶訪問相應網站時持久cookie的信息會被傳送給服務器。
也稱tracking cookie(跟蹤cookie)
Secure cookie(安全cookie)
僅在使用加密連接(比如HTTPS)時被使用。
HttpOnly cookie
...
總結起來:
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相關的操作。無非就是設置、讀取、刪除緩存,以及設置緩存失效時間。
文章列表