PHP面试题:使用PHP采集中超球员射门排行榜,并对数据进行处理

/*
题目概述:
请使用PHP采集新浪2014年中超赛季球员射门排行榜,保存到本地数据库,并按要求查询出来显示在页面上。

2014年中超赛季球员射门排行榜网址:
http://match.sports.sina.com.cn/football/csl/opta_rank.php?item=shoot&year=2014&lid=8&type=1&dpc=1
*/

// 步骤1:使用PHP自带函数获取排行榜页面的完整HTML内容,保存在工作目录,命名为shoot.html。

Select Code
1
2
3
4
5
6
7
8
9
10
$url = "http://match.sports.sina.com.cn/football/csl/opta_rank.php?item=shoot&year=2014&lid=8&type=1&dpc=1";

if (!file_exists('./data')) {
	mkdir('./data');
}

$filename = "./data/"."shoot.html";

$str = file_get_contents($url);
file_put_contents($filename, $str);

// 步骤2:使用PHP自带函数读取shoot.html的内容,并使用正则表达式匹配出这50个球员的所有数据(排名、姓名、球队、射门数、左脚、右脚、头球、其它部位),保存到一个二维数组$shoot_arr。

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
include './01.php';

$data = file_get_contents($filename);

// 正则表达式匹配球员数据

/*
排名、姓名、球队、射门数、左脚、右脚、头球、其它部位
*/
$pattern = '/(<tr  (class="g" )?>)\s+\<td\>(\d+)\<\/td\>.*?blank\"\>(.*?)\<\/a\>.*?blank\"\>(.*?)\<\/a\>.*?td\>(\d+)\<.*?td\>(\d+)\<.*?td\>(\d+)\<.*?td\>(\d+)\<.*?td\>(\d+)\<.*?(<\/tr>)/s';

$res = preg_match_all($pattern, $data, $match);

/*
排名、姓名、球队、射门数、左脚、右脚、头球、其它部位
3,4,5,6,7,8,9,10
*/
$shoot_arr = array();
// $keys = array('排名', '姓名', '球队', '射门数', '左脚', '右脚', '头球', '其它部位');
for ($i=0; $i < 50; $i++) { 
		$shoot_arr[$i] = array($match[3][$i], $match[4][$i], $match[5][$i], $match[6][$i], $match[7][$i], $match[8][$i], $match[9][$i], $match[10][$i]);
}

/*步骤3:随机打乱所有球员数据($shoot_arr)排列顺序,取出打乱顺序后前3个球员的数据保存到数组$rand_top3_arr(数组结构与$shoot_arr相同,键值从0开始)。*/

Select Code
1
2
3
4
5
6
7
8
9
10
11
include './02.php';

// 随机打乱
shuffle($shoot_arr);

// echo "<pre>";
// print_r($shoot_arr);
// echo "</pre>";

// 截取数组
$rand_top3_arr = array_slice($shoot_arr, 0, 3);

/*步骤4:遍历所有球员数据($shoot_arr),取出左脚射门次数在10至50之间的球员数据,并按照左脚射门次数从高到低排列,保存到数组$left_10to50_arr(数组结构与$shoot_arr相同,键值从0开始)。*/

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
include './02.php';

$left_10to50_arr = array();
foreach ($shoot_arr as $key => $value) {
	if ($value[4]>=10 && $value[4]<=50) {
		$left_10to50_arr[] = $value;
	}
}

// var_dump($left_10to50_arr);

foreach ($left_10to50_arr as $key => $value) {
	$a1[] = $value[4];
}

// var_dump($a1);
array_multisort($a1, $left_10to50_arr);
$left_10to50_arr = array_reverse($left_10to50_arr);

/*
步骤5:使用电脑桌面上的SQLyog数据库管理软件连接Mysql数据库,在test_db中创建数据表shoot_tbl,用于保存后续步骤保存$shoot_arr。
*/

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
数据库:test_db
SQL建表:shoot_tbl
CREATE TABLE `shoot`(
	`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
	`ranking` INT UNSIGNED NOT NULL,
	`player` VARCHAR(30) NOT NULL,
	`team` VARCHAR(30) NOT NULL,
	`shots` INT UNSIGNED NOT NULL,
	`lleg` INT UNSIGNED NOT NULL,
	`rleg` INT UNSIGNED NOT NULL,
	`head` INT UNSIGNED NOT NULL,
	`other` INT UNSIGNED NOT NULL
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*
步骤6:在PHP程序中将$shoot_arr的数据保存到上一步骤建立的shoot_tbl,要求一个球员为一条记录,一个属性为一个字段。
*/

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
include './02.php';
include '../DB/Model/config.php';
include '../DB/Model/Model.class.php';
// var_dump($shoot_arr);

$model = Model::getModel('shoot_tbl');

$data = array();
foreach ($shoot_arr as $key => $value) {
	$data['ranking'] = $value[0];
	$data['player'] = $value[1];
	$data['team'] = $value[2];
	$data['shots'] = $value[3];
	$data['lleg'] = $value[4];
	$data['rleg'] = $value[5];
	$data['head'] = $value[6];
	$data['other'] = $value[7];
	
	$model->add($data);
}

/*
步骤7:在PHP程序中使用SQL语句查询出上榜球员最多的前5个球队和对应的球员数,保存到数组$top_team5中。
*/
// SQL查询

/*
1、连接数据库
2、设置字符集
3、SQL语句
4、执行SQL语句
5、结果查询
*/

Select 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
<?php
$conn = mysqli_connect('localhost', 'root', '', 'test_db') or die("数据库连接失败:".mysqli_error());
mysqli_set_charset($conn, 'utf8');
$sql = "SELECT * FROM shoot_tbl";
$result = mysqli_query($conn, $sql);

$list = array();
if ($result) {
	while ($row = mysqli_fetch_assoc($result)) {
		$list[] = $row;
	}
}

// var_dump($list);

// 查找出球队
$team = array();
foreach ($list as $key => $value) {
	$team[] = $value['team'];
}

$team = array_unique($team);

// var_dump($team);

$top_team = array();
foreach ($team as $key => $value) {
	// 查找球员数
	$top_team[$value] = findtop5($value);
}
// 对键值排序
arsort($top_team);
$top_team5 = array_slice($top_team, 0, 5);
// var_dump($top_team5);

function findtop5($team){
	$conn = mysqli_connect('localhost', 'root', '', 'test_db') or die("数据库连接失败:".mysqli_error());
	mysqli_set_charset($conn, 'utf8');
	$sql = "SELECT * FROM shoot_tbl WHERE `team` = '{$team}'";
	$res = mysqli_query($conn, $sql);
	return mysqli_num_rows($res);
}
?>

/*
步骤8:写一个Class,该Class的构造函数传入参数$shoot_arr,返回一个重新排序的数组(数组结构与$shoot_arr相同,键值从0开始)。实例化并执行该Class,将结果保存到$sort_arr。排序规则如下:
1. 所有球员按左脚射门次数从高到低排列
2. 左脚射门次数相同的,再以右脚射门次数从高到低排序
3. 左右脚射门次数均相同的,再以头球次数从高到低排序
*/

Select 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
include './02.php';

class PlayerSort
{
	private $shoot;
	
	function __construct($arr)
	{
		$this->shoot = $arr;
	}

	function fun(){
		foreach ($this->shoot as $key => $value) {
			$a1[] = $value[4];
			$a2[] = $value[5];
			$a3[] = $value[6];
		}

		array_multisort($a1, $a2, $a3, $this->shoot);

		return $this->shoot;
	}
}

$data = new PlayerSort($shoot_arr);
$res = array_reverse($data->fun());

/*
步骤9:C:/web/include/class.quickskin.php是Quickskin模板引擎,在PHP中调用该Class,使用下图样式输出模板,将$shoot_arr、$rand_top3_arr、$left_10to50_arr、$top_team5、$sort_arr等数据输出。
*/

Select 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
// 使用Smarty模板引擎

include './02.php';
include './03.php';
include './04.php';
include './07.php';
include './08.php';

include '../libs/Smarty.class.php';

$tpl = new Smarty();

// 设置模板目录
$tpl->template_dir = "./views";

// 设置编译目录
$tpl->compile_dir = "./runtime/views_c";

// 设置缓存目录
$tpl->cache_dir = "./runtime/cache";

// 开启缓存
$tpl->caching = true;

// 设置缓存时间
$tpl->cache_lifetime = 10;

$tpl->assign('shoot_arr', $shoot_arr);
$tpl->assign('rand_top3_arr', $rand_top3_arr);
$tpl->assign('left_10to50_arr', $left_10to50_arr);
$tpl->assign('top_team5', $top_team5);
$tpl->assign('sort_arr', $sort_arr);

$tpl->display('./views/test.tpl');

模板文件:

Select 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
...
<table border="1" width="600">
	<caption><h3>50个球员的所有数据</h3></caption>
	<tr>
		<th>ranking</th>
		<th>player</th>
		<th>team</th>
		<th>shots</th>
		<th>lleg</th>
		<th>rleg</th>
		<th>head</th>
		<th>other</th>
	</tr>
	{foreach $shoot_arr as $val}    
		<tr>
			<td>{$val[0]}</td>
			<td>{$val[1]}</td>
			<td>{$val[2]}</td>
			<td>{$val[3]}</td>
			<td>{$val[4]}</td>
			<td>{$val[5]}</td>
			<td>{$val[6]}</td>
			<td>{$val[7]}</td>
		</tr>
	{/foreach}
</table>
...

源码:https://github.com/webjust/case-study

自定义一个模板引擎类的5步发展史

##第1版:代码混编(PHP和HTML混编)

Select 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
<table border="1" width="350">
	<caption><h2>用户表</h2></caption>
	<tr>
		<th>ID</th>
		<th>Username</th>
		<th>AddTime</th>
	</tr>
	<?php
		/*第1版:代码混编*/
		$link = mysqli_connect('127.0.0.1', 'root', '', 'ishop') or die("数据库连接失败:".mysqli_error());
		mysqli_set_charset($link, 'utf8');

		$sql = "SELECT * FROM `shop_user`";
		$res = mysqli_query($link, $sql);

		if($res){
			while($row = mysqli_fetch_assoc($res)){
				echo "<tr>";
				echo "<td>{$row['id']}</td>";
				echo "<td>{$row['username']}</td>";
				echo "<td>".date('Y-m-d H:i:s', $row['addtime'])."</td>";
				echo "</tr>";
			}
		}

		mysqli_close($link);
	?>
</table>

##第2版:高级分离。页面顶部放PHP代码处理业务逻辑

Select 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
<?php
	$link = mysqli_connect('127.0.0.1', 'root', '', 'ishop') or die("数据库连接失败:".mysqli_error());
	mysqli_set_charset($link, 'utf8');

	$sql = "SELECT * FROM `shop_user`";
	$res = mysqli_query($link, $sql);

	if($res){
		while($row = mysqli_fetch_assoc($res)){
			$userlist[] = $row;
		}
	}
?>

<!-- 省略部分html代码 -->
<table border="1" width="350">
	<caption><h2>用户表</h2></caption>
	<tr>
		<th>ID</th>
		<th>Username</th>
		<th>AddTime</th>
	</tr>
	<?php foreach($userlist as $v): ?>
		<tr>
			<td><?php echo $v['id'] ?></td>
			<td><?php echo $v['username'] ?></td>
			<td><?php echo $v['addtime'] ?></td>
		</tr>
	<?php endforeach; ?>
</table>
<!-- 省略部分html代码 -->

<?php mysqli_close($link); ?>

##第3版:PHP和HTML页面分离技术, 使用include引入模板文件

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
	/* 第3版:PHP和HTML页面分离技术, 使用include引入模板文件 */
	$link = mysqli_connect('127.0.0.1', 'root', '', 'ishop') or die("数据库连接失败:".mysqli_error());
	mysqli_set_charset($link, 'utf8');

	$sql = "SELECT * FROM `shop_user`";
	$res = mysqli_query($link, $sql);

	if($res){
		while($row = mysqli_fetch_assoc($res)){
			$userlist[] = $row;
		}
	}
	
	// 使用include引入模板文件
	include './test.tpl';

	mysqli_close($link);
?>

问题:所有的变量都可以在模板文件中输出。

解决问题:实现PHP和页面代码的分离

1. PHP分配数据给HTML页面(使用对象来传递变量和显示模板,可以解决数据安全问题)
2. HTML页面,由美工负责,不能出现PHP代码

##第4版:封装1个模板类,使用对象的方式分配数据和显示模板

PHP文件:引入模板引擎类,传递变量

Select Code
1
2
3
4
5
6
7
8
// 引入模板引擎类
include './MySmarty.class.php';

// 创建1个模板对象
$tpl = new MySmarty();

$tpl->assign('userlist', $userlist);
$tpl->display('./test.tpl');

模板引擎类:

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class MySmarty
{
	// 定义一个数组存储变量
	public $data = array();

	public function assign($key, $val)
	{
		$this->data[$key] = $val;
	}

	public function display($tpl)
	{
		// var_dump($this->data);    //测试$data是否存储了传过来的变量
		foreach ($this->data as $key => $val) {
			// 把数组里的值$data['userlist'],传递到该方法内$$key等同于$userlist
			$$key = $val;
		}

		// 引入模板,遍历$userlist的数组
		include $tpl;
	}
}

##第5版:完成模板文件的编译过程

PHP文件:

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
	/* 第5版:完成模板文件的编译过程 */

	// 引入模板引擎类
	include './MySmarty.class.php';

	// 创建1个模板对象
	$tpl = new MySmarty();
	$title = "这是一个测试标题";

	$tpl->assign('title', $title);
	$tpl->display('./test.tpl');
?>

模板引擎类:

这里完成了一个简单的编译,把模板中的变量{$title}编译成php代码:

Select 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
<?php 
	class MySmarty
	{
		// 定义一个数组存储变量
		public $data = array();

		public function assign($key, $val)
		{
			$this->data[$key] = $val;
		}

		public function display($tpl)
		{
			foreach ($this->data as $key => $val) {
				$$key = $val;
			}

			// 模板编译
			$filename = $this->compile($tpl);

			// 引入模板,遍历$userlist的数组
			include $filename;
		}

		// 这里进行一个简单的编译,把{$title}编译成php代码(部分省略): php echo $title
		public function compile($tpl)
		{
			$tpl_String = file_get_contents($tpl);

			$preg = "/\{(.*?)\}/";
			$match = "<?php echo $1; ?>";

			// 文件后缀随便取名
			$filename = './template_c/'.md5($tpl).".php";

			$new_String = preg_replace($preg, $match, $tpl_String);
			
			//如果目录不存在则,创建编译后的存放目录
			if (!file_exists('./template_c/')) mkdir('./template_c/');
			file_put_contents($filename, $new_String);

			return $filename;
		}
	}

模板文件:

Select Code
1
2
3
4
5
6
7
8
9
//编译前:test.tpl
<body>
	<?php echo $title ?>
</body>

//编译后:aa757471baa99de5a5035424b8cad130.php
<body>
	<?php echo $title ?>
</body>

完成编译后的代码,实际上还是PHP代码,然后通过display()方法,显示给前台。

以上就是模板引擎的5步发展史,对于理解Smarty等模板引擎有学习的帮助。

源码:https://github.com/webjust/case-study

第一次安装nodejs

安装Node环境:

新版本默认已经安装了NPM,NPM 包管理 Node Package Manager
官网:https://nodejs.org/

http://nodejs.cn/
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。Node.js 的包管理器 npm,是全球最大的开源库生态系统。

测试nodejs安装步骤:

1、测试npm -v
第一次安装nodejs

2、新建D:/test.js文件

Select Code
1
2
3
4
5
6
7
var http = require("http"); 
http.createServer(function(request, response) { 
response.writeHead(200, {"Content-Type": "text/plain"}); 
response.write("test nodjs"); 
response.end(); 
}).listen(8899); 
console.log("nodejs start listen 8899 port!");

3、运行node test.js文件,http://127.0.0.1:8899/即可以访问。
nodejs02

nodejs03