PHP面向对象深入--构造函数中调用成员函数,到底调用的是哪个类的函数

15 Dec 2015 Category: PHP

PHP中,静态方法中不能使用$this,即用双冒号::调用的函数。这里,构造函数要除外,那么, 构造函数中的$this到底指向谁,$this->display函数又会执行那个类里面的函数?

如果去看Yii2的源码,发现Yii2的源码都是在构造函数中调用一个init函数对类进行初始化 的操作。但是如果看Application.php的源码,发现在构造函数__construct里面并没有显示 的调用init函数,也没有调用其父类的构造函数,而是调用Compoment的构造函数,继续追踪, 发现Application 继承 Module, Module继承ServiceLocator, ServiceLocator继承Component, Component继承Object, 而在Object的构造函数中调用了init函数。

    public function __construct($config = []) {
        if (!empty($config)) {
            Yii::configure($this, $config);
        }
        $this->init();
    }

那么,这里的$this指向Application还是Object本身呢?为此,用下面的程序进行试验 从结果可以看出,$this指向的是调用者本身。 如果父类的display方法是public或者protected(即可以被子类继承),执行的是调用类,否则 就是当前所在类的成员函数。但是这种情况下,display函数里面打印$this依然是调用类。


<?php
class MyObj {

    function __construct() {
        $this->display();
        var_dump($this);//输出MyApp
    }

    public function display(){//修饰符是private的时候执行该函数,否则执行子类函数
        var_dump($this);//输出MyApp,无论方法修饰符是private 还是public,protected
        echo 'obj';
    }

}
class Modle extends MyObj {

}

class Compoment extends Modle {

}
class MyApp extends Compoment {
    function __construct() {
        Modle::__construct();
    }

    public function display(){//
        echo 'obj test public';
    }
}

$objOther = new MyApp();
?>

理解(此处仅仅是个人理解) :

  • 如果父类的diplay函数是可以被继承的,且子类中重写了该函数,则调用的是子类 的display函数。其他情况,不可继承或子类并没与重写该函数,执行父类函数。

  • 对于$this的指向,$this总是指向最终构造的对象。因为这是构造函数。

查看更多

Node.JS 回调函数在请求已经结束之后是否会执行?

14 Dec 2015 Category: Developer

做PHP,程序都是顺序执行的,所谓回调函数,其本身也是顺序执行的。但是Node.js 本身是 回调函数是异步执行的。PHP中,如果请求结束,那么在改请求所执行的操作都结束了。那么 Node.js里呢。

我想用node.js搭建一个服务程序,然后采集其他站点的数据,因为很多站点的数据都需要js 解析。那么问题来了,我通过linux cron 定时访问node服务,node服务向我要采集的站点 发起网络请求,并在网络请求的回调中完成数据采集的操作。那我的node服务是要等待数据采 集完才给出请求反馈么?如果我需要在一次cron 访问中采集多个站点的数据,那么,等待这些 异步采集全部执行完再返回吗?如果我发起调用万采集函数之后,直接返回cron结果,通知 cron请求已经被执行了,已经发起的采集请求还会执行到回调函数里吗?这就是题目中的问题, Node.JS 回调函数在请求已经结束之后是否会执行?

当然,答案是肯定的(已经试验过了)。Node.js的异步进程(或线程,此处并不确定是哪个), 并不是随着浏览器的访问的结束而结束。每个http请求和http请求中所发起的其他操作都是 平级的,并没有从属关系,Node.js本身维护这些进程,因此除非node服务down了,否则回调 都会执行。

对比PHP,如果用PHP抓取多个页面,那么PHP就得等待所有抓取工作完成之后才能够通知执行人, 已经执行了抓取工作。但是,数据抓取本身就有很多的不确定性,如果人家更新了站点, 更新了域名,或者某个站点请求时间长都会造成PHP本身请求超时。但是Node.js 就可以完全非 阻塞的运行,只要发出抓取请求就可以通知执行者已经成功执行了抓取请求。

查看更多

PHP 浏览器关闭 和timeout之后,php程序还会执行吗?

13 Dec 2015 Category: PHP

在 PHP 内部,系统维护着连接状态,其状态有三种可能的情况: 0 - NORMAL(正常) 1 - ABORTED(异常退出) 2 - TIMEOUT(超时) 当 PHP 脚本正常地运行 NORMAL 状态时,连接为有效。当远程客户端中断连接时,ABORTED 状态的标记将会被打开。当连接时间超过 PHP 的时限时,TIMEOUT 状态的标记将被打开。


<?php
set_time_limit(60);
$interval = 5;
do {

    $fp = fopen('text3.txt', 'a');

    fwrite($fp, "test\n\r");

    fclose($fp);

    sleep($interval); // 函数延迟代码执行若干秒

} while (true);

?>

对于上述代码,如果我一打开就立刻关掉该页面,应该会在文件里写多少次test呢?事实证明, 当服务器ignore_user_abort开启的时候,执行12次,就是说,即时用户关掉了 浏览器,PHP脚本也会只执行完,除非遇到timeout的情况。 当ignore_user_abort关闭的时候,会随着用户的终端而终端脚本

注意,面对这种情况,必须确认服务器ignore_user_abort的设置是有效的,xampp似乎无效

查看更多

SSO 登陆简单实现

11 Dec 2015 Category: PHP

SSO又称单点登录。简单的说就是多个站点应用(域名不一定在同一个主域名下),在任意一 个子系统中登录,其他系统都实现登陆。同样,一个站点退出,其他站点也退出。

SSO 登录的难点在于如何保持多个系统同步。

实现SSO登陆的步骤如下:

  • 子系统点击登陆,带上redirect作为返回路径跳转到sso统一登录页面(passport)进行 登录。

  • 用户在passport登录之后(写入passport的session,cookie等登录信息),通过js向各个 子系统的登录接口(并带上passport的临时票据ticket)

  • 子系统的登录接口中,确定请求的合法性,并根据请求的临时票据ticket请求passport服务器获 取用户信息(这里是服务器跟服务器直接通讯,因此passport需要能够根据ticket获取到用户信息), 并将用户信息写入登陆状态中,登录用户,写子系统的session,cookie

  • 所有子系统写完之后,根据一开始请求passport的跳转参数redirect进行跳转条主, 回到登录前的子系统

退出过程:

  • 子系统点击退出,跳转到passport的退出操作,并生存退出票据ticket

  • passport退出页面中可通过ajax请求各个子系统

  • 各个子系统判断ticket的合法性,退出用户

上面的就是整个sso的过程,其实这里面还有一些方面没处理。所有的登录登出操作都是在 passport处理的,因此数据同步是单项性的。如果各个登录登出操作是在每个子系统本身处 理的,那就需考虑client到passport的状态同步。同时,单点登录系统需要对参数进行加密, 子系统和passport直接可以根据同样的key对参数进行ras加密,保证数据传输过程中不被破解

个人代码可参见github

查看更多

Yii2 CMS 系统 DanDan CMS开发基本完成

27 Nov 2015 Category: PHP

最近几天,忙于完成DanDan CMS,博客更新速度有些慢。今天基本完成。DanDan CMS系统源码 https://github.com/froyot/dandan

DanDan CMS系统采用YII2 basic版本进行开发,实现多模块,多主题,内容发布等基础功能。 整个系统写下来,还是需要考虑很多东西,各个模块之间的联系,代码的统筹,结构划分等 都是一门学问

对于一个具有前台,后台的内容发布系统,model层如何划分?是先按业务划分还是按结构划 分呢?如果按结构,直接前后台模块分开,各自一个model,那么问题又来了,哪些是可以共用 的,哪些是需要区分的。如果按业务划分model\db下直接对接数据库,不做业务处理, model\action负责主要业务处理,不管前台后台,所有的操作都经过models\action进行。 我比较推崇这种处理方式,我觉得,一个model所具有的属性,操作是其本身所具有的,不会 因为其运用环境不同而不同,只是需要根据不同的环境应用不同的属性。但是这样也有问题, 后台常常因为运营需要,需要连接不同的表,但是前台则不需要。后台需要添加一些临时属性, 以便于数据的编辑,但前台不需要,这个时候就需要根据结构进行划分。

在写DanDan CMS系统的时候,刚开始,一切都不太清晰,这个时候比较凌乱,理不清头像。 但是这个时候的代码却是基础,如果这个时候基础设计不和里,等整个系统出来之后,就会 明显觉得当初结构设计是多么蹩脚,以至于束缚现有的开发。在DanDan CMS中就有这样的体会, 因此后期会进行很大的调整,但是所有的调整都是缓慢进行,不会一次性进行大的重构,保证 每次改变都是在整个体系清晰的情况下进行。

查看更多

Yii2 中使用registerJsFile引入asset发布的js文件

14 Nov 2015 Category: PHP

Yii2 提供AssetManage对一些不对外的目录的静态文件发布到可访问的asset目录中。如果 所有的静态文件都通过AssetBundle的方式进行引入,那倒没什么值得说的了。不过像js文 件,由于依赖关系,有时候不得不在文件开头就引入某个js文件,同时其他js文件依然在文 件底部加载。这个时候我们不得不在模板或者视图文件中使用registerJsFile对js文件进行 引入。

但是,在视图或者模板文件中,如果我们想直接引入web目录下的js文件直接采用别名@web 就可以,但是如果想要访问asset目录呢? 假设模板文件中,注册了AssetBundle $bundel, 采用下面的方式引入改AssetBundle 中发 布的asset 静态文件


<?php
    $manager = $this->getAssetManager();
    $url = $manager->getAssetUrl($bundel,'js/vue.min.js');
    $this->registerJsFile($url,['position'=>yii\web\View::POS_HEAD]);
?>

查看更多

SSH 防止暴力破解

10 Nov 2015 Category: Developer

最近看服务器日志,发现secure日志中有很多登录验证记录,估计是被盯上了。ssh暴力扫 描破解。ssh本身是几次密码错误之后就断开,但是还可以继续连接,然后再试密码和端口。 如果ssh扫描频率大,自己就会没办法连接到服务器,因为服务器一直在验证那些实验的密码。

在网上看到一个脚本,用来将多次密码错误尝试的ip拉进小黑屋。从此拒绝改ip的请求。

#!/bin/bash
#Denyhosts ssh error ip

cat /var/log/secure|awk '/Failed/{print $(NF-3)}'|sort|uniq -c|awk '{print $2"=" $1;}' >/root/bin/Denyhosts.txt
DEFINE="3"
for i in `cat /root/bin/Denyhosts.txt`
do
IP=`echo $i|awk -F= '{print $1}'`
NUM=`echo $i|awk -F= '{print $2}'`
if [ $NUM -gt $DEFINE ]
then
grep $IP /etc/hosts.deny >/dev/null
if [ $? -gt 0 ];
then
echo "sshd:$IP" >> /etc/hosts.deny
fi
fi
done

以上代码在不同的服务器需要适当修改。 在我的虚拟机里面,log里没有secure文件,登陆记录在auth.log里。 另外需要保证/root/bin/Denyhosts.txt文件存在。

存在一个问题,因为即使公司里很多都是使用动态ip的,万一哪天分配的ip跟黑名单里 面的ip一样,那就无力回天了。

查看更多

PHP POST 数组限制

07 Nov 2015 Category: PHP

今天调一个接口,测试批量上传数据。上传数据用的是POST方式,分成一个多维数组上传。 但是,问题来了,最多可以批量上传多少条数据?PHP默认POST数据的限制是2M,但是并不代 表你就可以真的传2M以内的任意长度的数组。

PHP对输入变量是有限制的。默认php.ini里的max_input_vars的限制是1000.如果POST一个二维数组,每个二维子数组里有五个元素,那么,只能POST200个子数组。那 么多余的整么办?直接抛掉。。。。。真是简单粗暴。那么抛掉的策略是怎么样的呢?比如我POST上述数组210个,另外还POST一个变量type,那么服务器的$_POST参数里有啥?

一下数据是通过PHP CURL方式提交的,其他语言提交之后数据顺序不一定不变

$data['ztype'] = "ztype";
$data['aber'] = "aber";
$data['User'] = [];
for($i=0;$i<290;$i++)
{
    $data['User'][] = [
        'create_time'=>1,
        'health_action_cat_id' => '5',
        'detail' => '{"distance":44}',
        'uid' => '5089',
        'provider_id'=>$i,
    ];
}

如果提交的数组是这样的,那么,服务器的$_POST是这样的:

{
    "ztype":"ztype",
    "aber":"aber",
    "User": [0-198]
}

那如果顺序变一下呢,

$data['User'] = [];
for($i=0;$i<290;$i++)
{
    $data['User'][] = [
        'create_time'=>1,
        'health_action_cat_id' => '5',
        'detail' => '{"distance":44}',
        'uid' => '5089',
        'provider_id'=>$i,
    ];
}
$data['ztype'] = "ztype";
$data['aber'] = "aber";

服务器的$_POST是这样的:

{
    "User": [0-199]
}

额,这个…难怪容易出现诡异的情况。像其他语言(比如java),提交之后数据顺序是不确定的,所以,所以就会出现数据混乱,在获得1000个变量之后,PHP将抛弃后面的输入。如果下一个数组大小加起来已经超过1000,PHP也会果断抛弃….

查看更多