从零开始打造自己的PHP框架——第1章

文章目录
  1. 1. 目标
  2. 2. 类库自动加载
    1. 2.1. 常规加载
    2. 2.2. 自动加载
    3. 2.3. 自动加载进阶
    4. 2.4. 加载机制简析
  3. 3. 路由控制
    1. 3.1. 隐藏index.php
    2. 3.2. 获取URL中的控制器和方法
    3. 3.3. 获取URL中的参数
    4. 3.4. 支持localhost
  4. 4. 源码分享
  5. 5. 书签

目标

本篇,我们来实现类库自动加载,以及路由解析。

类库自动加载

常规加载

常规加载一般使用include或者require,它们最根本的区别在于错误处理的方式不一样。

include包括并运行指定文件。include一个文件存在错误的话,那么程序不会中断,而是继续执行,并显示一个警告错误。

include_once的作用和include几乎相同,唯一的差别在于导入之前会检查要导入的文件是否已经被导入过了,如果有的话就不会再次重复导入。

require会将目标文件的内容读入,并且把本身替换成这些读入的内容。require一个文件存在错误的话,那么程序就会中断执行了,并显示致命错误。

require_once的作用和require几乎相同,唯一的差别在于导入之前会检查要导入的文件是否已经被导入过了,如果有的话就不会再次重复导入。

在使用一个文件(类库)的函数之前,我们需要先使用include或者require,把该文件引入进当前文件,然后才能使用文件中的函数。

例如我们要新建一个route对象。
1、core目录中,新建route.php:

1
2
3
4
5
6
7
8
9
10
<?php
/**
* 路由控制
*/
namespace core;
class route{
public function __construct(){
echo 'route is ready!';
}
}

2、根目录下index.php中,添加:

1
$route = new \core\route();

会报错Fatal error: Class ‘core\route’ not found in…

需要改成:

1
2
include '\core\route.php';
$route = new \core\route();

或者:

1
2
require '\core\route.php';
$route = new \core\route();

自动加载

1
bool spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] )

将函数注册到SPL __autoload函数队列中。如果该队列中的函数尚未激活,则激活它们。成功时返回 TRUE,失败时返回 FALSE。

spl_autoload_register的一般用法:

1
2
3
4
5
spl_autoload_register(function ($class_name) {
require_once $class_name . '.php';
});

$route = new \core\route();

在新建route对象时,class_name也就是\core\route会传入到spl_autoload_register函数中,该函数的参数是一个回调函数。回调函数拿到class_name,然后进行文件的引入。

也就是说,和常规加载相比,使用自动加载,我们不必对每一个类库单独进行引入。

自动加载进阶

上例中,spl_autoload_register的回调函数是一个匿名函数,而且比较简单。下面,我们来写一个更高级的回调函数。新建aotuload.php,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
/**
* 自动加载类库
*/
namespace core;

class autoload{
public static function load($class_name){
if(file_exists($class_name.'.php')){
require_once $class_name.'.php';
return true;
}else{
echo 'error: unable to load '.$class_name.'.php';
return false;
}
}
}

使用的时候,改成:

1
2
3
include CORE.'/autoload.php';
spl_autoload_register('\core\autoload::load');
$route = new \core\route();

加载机制简析

在使用include的时候,会用到php文件系统。在文件系统中访问一个文件有三种方式:

1、相对文件名形式如route.php。它会被解析为 include_path/route.php,其中 include_path 表示.;C:/laragon/bin/php/php-5.6.16/PEAR
假设当前目录是C:/laragon/www/vkphp,则该文件名依次被解析为:

  • C:/laragon/www/vkphp/route.php
  • C:/laragon/bin/php/php-5.6.16/PEAR/route.php

2、相对路径名形式如core/route.php,它会被解析为 include_path/core/route.php
假设当前目录是C:/laragon/www/vkphp,则该文件名依次被解析为:

  • C:/laragon/www/vkphp/core/route.php
  • C:/laragon/bin/php/php-5.6.16/PEAR/core/route.php

3、绝对路径名形式如/core/route.php,在linux系统中,它会被解析为/core/route.php;在windows系统中,它会被解析为 include_path/core/route.php,和相对路径一样。

绝对路径名形如C:/laragon/www/vkphp/core/route.php 或者C:\laragon\www\vkphp\core\route.php 或者 C:\\laragon\\www\\vkphp\\core\\route.php ,在windows系统中,会被解析为C:/laragon/www/vkphp/core/route.php。也就是说,windows中斜线和反斜线和双反斜线效果相同。

获取include_path和设置include_path的栗子:

1
2
3
echo get_include_path();
ini_set('include_path', ini_get('include_path').PATH_SEPARATOR.'lib_path/libs');
echo get_include_path();

路由控制

隐藏index.php

1、访问地址 http://vkphp.dev/index.php ,此时,我们看到“helloworld”和“route is ready!”。

2、访问地址 http://vkphp.dev/index.php/index/index ,可以看到同样的信息。

3、访问地址 http://vkphp.dev/index/index ,则会报404错误。那么,我们怎样隐藏掉index.php呢?答案是添加.htaccess。

在项目根目录下,添加.htaccess,内容如下:

1
2
3
4
5
6
7
8
9
10
Options +FollowSymLinks  
IndexIgnore */*
RewriteEngine on

# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

# otherwise forward it to index.php
RewriteRule . index.php

4、访问地址 http://vkphp.dev/index/index ,可以看到和1、2中相同的信息。

获取URL中的控制器和方法

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
<?php
/**
* 路由控制
*/
namespace core;

class route{
public $ctrl;
public $action;
public function __construct(){
//echo 'route is ready!';

/**
* 1、隐藏index.php
* 2、获取URL中的控制器和方法
*/

if(isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI'] != '/'){
$path = $_SERVER['REQUEST_URI'];
$patharr = explode('/',trim($path, '/'));
p($patharr);

if(isset($patharr[0])){
if($patharr[0] != 'index.php'){
// 省略了index.php
$this->ctrl = $patharr[0];

if(isset($patharr[1])){
$this->action = $patharr[1];
} else{
$this->action = 'index';
}
}else{
// 没省略index.php
if(isset($patharr[1])){
$this->ctrl = $patharr[1];
}
if(isset($patharr[2])){
$this->action = $patharr[2];
} else{
$this->action = 'index';
}
}
}else{
$this->ctrl = 'index';
$this->action = 'index';
}

}else{
$this->ctrl = 'index';
$this->action = 'index';
}
}
}

访问地址 http://vkphp.dev/index/index 或者 http://vkphp.dev/index.php/index/index ,即可看到打印出的patharr信息。

获取URL中的参数

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
59
60
61
62
63
64
65
66
67
68
69
70
<?php
/**
* 路由控制
*/
namespace core;

class route{
public $ctrl;
public $action;
public $params=array();
public function __construct(){
//echo 'route is ready!';

/**
* 1、隐藏index.php
* 2、获取URL中的控制器和方法
* 3、获取URL中的参数
*/

if(isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI'] != '/'){
$path = $_SERVER['REQUEST_URI'];
$patharr = explode('/',trim($path, '/'));
//p($patharr);

if(isset($patharr[0])){
if($patharr[0] != 'index.php'){
// 省略了index.php
$this->ctrl = $patharr[0];

if(isset($patharr[1])){
$this->action = $patharr[1];
} else{
$this->action = 'index';
}
$count = count($patharr);
$i=2;
while($i < $count){
$this->params[$patharr[$i]] = $patharr[$i+1];
$i = $i + 2;
}
}else{
// 没省略index.php
if(isset($patharr[1])){
$this->ctrl = $patharr[1];
}
if(isset($patharr[2])){
$this->action = $patharr[2];
} else{
$this->action = 'index';
}

$count = count($patharr);
$i=3;
while($i < $count){
$this->params[$patharr[$i]] = $patharr[$i+1];
$i = $i + 2;
}
}
}else{
$this->ctrl = 'index';
$this->action = 'index';
}

}else {
$this->ctrl = 'index';
$this->action = 'index';
}
p($this->params);
}
}

访问地址 http://vkphp.dev/index/index/id/3/name/voidking 或者 http://vkphp.dev/index.php/index/index/id/3/name/voidking ,即可看到打印出的params信息。

支持localhost

访问地址 http://localhost/vkphp/index.php/index/index/id/3/name/voidking ,无法正常获取控制器、方法和参数,修改如下:

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
<?php
/**
* 路由控制
*/
namespace core;

class route{
public $ctrl='index';
public $action='index';
public $params=array();
public function __construct(){
//echo 'route is ready!';

/**
* 1、隐藏index.php
* 2、获取URL中的控制器和方法
* 3、获取URL中的参数
*/
if(isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI'] != '/' ){
$path = $_SERVER['REQUEST_URI'];
$patharr = explode('/',trim($path, '/'));
}else{
$patharr = array();
}

if(isset($_SERVER['HTTP_HOST']) && ($_SERVER['HTTP_HOST'] == 'localhost' || $_SERVER['HTTP_HOST'] == '127.0.0.1') ){
// 去掉项目名称
$patharr = array_slice($patharr,1,count($patharr)-1);
}
if(isset($patharr[0])){
if($patharr[0] == 'index.php'){
// 去掉index.php
$patharr = array_slice($patharr,1,count($patharr)-1);
}
if(isset($patharr[0])){
$this->ctrl = $patharr[0];
}
if(isset($patharr[1])){
$this->action = $patharr[1];
}

$count = count($patharr);
$i=2;
while($i < $count){
if(isset($patharr[$i+1])){
$this->params[$patharr[$i]] = $patharr[$i+1];
}
$i = $i + 2;
}
}

p($this->ctrl);
p($this->action);
p($this->params);
}
}

源码分享

https://github.com/voidking/vkphp/releases/tag/v1.1.0

书签

从零开始打造自己的PHP框架

PHP 檔案引入路徑問題