这个框架功能比较简单,只有url拦截及自定义、加载静态模板两个功能,之前的博客也是基于这个框架实现的。第一部分是记录实现这个框架所需的php基础知识,第二部分是这两个功能的实现方法。最后还有实现自动登录的最佳实践。
php基础相关
$_SERVER
服务器和执行环境信息
几个用到的项: * 'REQUEST_METHOD' : 访问页面使用的请求方法;例如,“GET”,“HEAD”,“POST”,“PUT”。 * 'REQUEST_URI' : URI 用来指定要访问的页面。例如 “/index.html”。
Date
1  | date(format,timestamp)  | 
:--- | : --- |
format | 必需。规定如何返回结果。|
timestamp | 可选。 |
格式化方式说明
| 格式化方式 | 说明 | 
|---|---|
| Y | 4位数字年,y为2位数字,如99即1999年 | 
| m | 数字月份,前面有前导0,如01。n 为无前导0数字月份 | 
| F | 月份,完整的文本格式,例如 January 或者 March | 
| M | 三个字母缩写表示的月份,例如 Jan 或者 Mar | 
| d | 月份中的第几天,前面有前导0,如03。j 为无前导0的天数 | 
| w | 星期中的第几天,以数字表示,0表示星期天 | 
| z | 年份中的第几天,范围0-366 | 
| W | 年份中的第几周,如第32周 | 
| H | 24小时格式,有前导0,h为12小时格式 | 
| G | 24小时格式,无前导0,g为对应12小时格式 | 
| i | 分钟格式,有前导0 | 
| s | 秒格式,有前导0 | 
| A | 大写上下午,如AM,a为小写 | 
例子 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
echo("Result with date():<br />");
echo(date("l") . "<br />");
echo(date("l dS \of F Y h:i:s A") . "<br />");
echo("Oct 3,1975 was on a ".date("l", mktime(0,0,0,10,3,1975))."<br />");
echo(date(DATE_RFC822) . "<br />");
echo(date(DATE_ATOM,mktime(0,0,0,10,3,1975)) . "<br /><br />");
echo("Result with gmdate():<br />");
echo(gmdate("l") . "<br />");
echo(gmdate("l dS \of F Y h:i:s A") . "<br />");
echo("Oct 3,1975 was on a ".gmdate("l", mktime(0,0,0,10,3,1975))."<br />");
echo(gmdate(DATE_RFC822) . "<br />");
echo(gmdate(DATE_ATOM,mktime(0,0,0,10,3,1975)) . "<br />");
结果: > Result with date(): Tuesday Tuesday 24th of January 2006 02:41:22 PM Oct 3,1975 was on a Friday Tue, 24 Jan 2006 14:41:22 CET 1975-10-03T00:00:00+0100
Result with
gmdate(): Tuesday Tuesday 24th of January 2006 01:41:22 PM Oct 3,1975 was on a Thursday Tue, 24 Jan 2006 13:41:22 GMT 1975-10-02T23:00:00+0000
字符串
explode()分割字符串,返回数组。 1
explode(delimiter, string)
随机字符串
- 预置一个的字符串 $chars ,包括 a – z,A – Z,0 – 9,以及一些特殊字符。
 - 在 $chars 字符串中随机取一个字符。
 - 重复第二步n次,可得长度为n的字符串。
 
1  | function generate_password( $length = 8 ) {  | 
加密
sha256 1
$password = hash('sha256', $identifier . $_POST['password']);
Session
每次用之前一定要session_start(): 1
2session_start();
$_SESSION[$var] = xxxx;
setcookie()函数用于设置 cookie。
语法 1
setcookie(name, value, expire, path, domain);
例子 在下面的例子中,我们将创建名为 "user" 的 cookie,把为它赋值 "Alex Porter"。我们也规定了此 cookie 在一小时后过期: 1
2
3
4
5
6
7
8
9 
setcookie("user", "Alex Porter", time()+3600);
<html>
<body>
</body>
</html>
注释:在发送 cookie 时,cookie 的值会自动进行 URL 编码,在取回时进行自动解码(为防止 URL 编码,请使用
setrawcookie()取而代之)。
PHP 的$_COOKIE变量用于取回 cookie 的值。 在下面的例子中,我们取回了名为 "user"的 cookie 的值,并把它显示在了页面上: 1
2
3
4
5
6
7
// Print a cookie
echo $_COOKIE["user"];
// A way to view all cookies
print_r($_COOKIE);
当删除 cookie 时,您应当使过期日期变更为过去的时间点。 删除的例子: 1
2
3
4 
// set the expiration date to one hour ago
setcookie("user", "", time()-3600);
变参
1  | 
  | 
反射
建立反射类
1  | $class = new ReflectionClass('Person');//建立 Person这个类的反射类  | 
获取属性(Properties)
1  | $properties = $class->getProperties();  | 
获取注释
1  | foreach($properties as $property) {  | 
获取类的方法
1  | if($class->hasMethod($methodName)){  | 
执行类的方法
1  | $instance->getBiography(); //执行Person 里的方法getBiography  | 
URI
uri结构:
apppath/class/method/paramsuri示例 :www.liuhe.website/index.php?/Articles/single/13
实现方法:
- 在
index.php中通过$_SERVER['REQUEST_URI']获取uri,然后获取className,methodName以及params。 - include相应控制器的文件,即
controller/$className.php。 - 建立反射类,调用相关method传入相应参数。
 - 防止直接通过文件目录访问(通过服务器配置rewrite实现)
 
code: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58/**
 * Parse uri , instantiate the class
 * and invoke the method
 */
public function parseRequestUri()
{
    if (isset($_SERVER['REQUEST_URI'])) {
        $this->uri_string = $_SERVER['REQUEST_URI'];
        $this->segments = explode('/', $this->uri_string);
        // var_dump($this->segments);
        /*
         * get the class name, method name and the params
         */
        if (count($this->segments) <= 2) {
            $className = 'Articles';
            $methodName = 'home';
        } else {
            $className = $this->segments[2];
            if (isset($this->segments[3])) {
                $methodName = $this->segments[3];
            } else {
                // 404 Error
                $this->_404Error("Empty Method");
            }
        }
        if ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'POST') {
            $params = array();
            $len = count($this->segments);
            if ($len > 4) {
                for ($i = 4; $i < $len; $i++) {
                    array_push($params, $this->segments[$i]);
                }
            }
            $classFile = "controller/$className.php";
            if (!file_exists($classFile)) {
                // 404 error
                $this->_404Error("File Not Found");
                return;
            }
            /*
             * build reflection class, invoke the method
             */
            require "$classFile";
            $rc = new ReflectionClass($className);
            if (!$rc->hasMethod($methodName)) {
                // 404 error
                $this->_404Error("Method Not Found");
                return;
            }
            $instance = $rc->newInstance();
            $method = $rc->getMethod($methodName);
            $method->invokeArgs($instance, $params);
        }
    } else {
        echo "not set REQUEST_URI";
        return;
    }
}
加载静态模板(带参数)
实现方法: * 定义参数结构(占位符): * i 为第i个参数 * 把模板文件加载到内存,用字符串替换函数替换上述占位符。
code: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22/**
* Load a static template
*
* @var string, array
*/
public function load($page = '', $args = array())
{
    if (empty($page)) {
        return;
    }
    $fileName = "static/$page";
    $file = fopen($fileName, "r");
    $content = fread($file, filesize($fileName));
    if (!empty($args)) {
        $i = 0;
        while (strpos($content, "{{args[$i]}}") !== false) {
            $content = str_replace("{{args[$i]}}", $args[$i], $content);
            $i += 1;
         }
     }
     echo "$content";
}
实现自动登录
以下部分来自StackOverflow
- When the user successfully logs in with Remember Me checked, a login cookie is issued in addition to the standard session management cookie.
 - The login cookie contains a series identifier and a token. The series and token are unguessable random numbers from a suitably large space. Both are stored together in a database table, the token is hashed (sha256 is fine).
 - When a non-logged-in user visits the site and presents a login cookie, the series identifier is looked up in the database.
- If the series identifier is present and the hash of the token matches the hash for that series identifier, the user is considered authenticated. A new token is generated, a new hash for the token is stored over the old record, and a new login cookie is issued to the user (it's okay to re-use the series identifier).
 - If the series is present but the token does not match, a theft is assumed. The user receives a strongly worded warning and all of the user's remembered sessions are deleted.
 - If the username and series are not present, the login cookie is ignored.
 
 
This approach provides defense-in-depth. If someone manages to leak the database table, it does not give an attacker an open door for impersonating users.