Archive for the ‘笔记’ Category

PHP中静态调用非静态方法

Sunday, March 6th, 2011

1.PHP中可以静态调用非静态方法么?

今天我被问到PHP中可不可以使用 className::methodName() 的方法来调用一个没有声明Static的方法。在我的印象中,我好像是见过这种用法,但又有些不确定。大家都知道,在手册或者教程里,方法被分为静态方法和非静态方法,通常我们静态调用的方法,肯定是静态方法。

那如果我们调用了非静态方法会怎么样呢?首先做测试.

<?php
class test{

function test(){

echo ‘it works’;

}

}

test::test();

执行以下,返回错误如下

Fatal error: Non-static method test::test() cannot be called statically in /home/×××/test.php on line 7 Call Stack: 0.0002 332548 1. {main}() /home/×××/test.php:0

这个时候,可能大家就会认为静态调用非静态方法是行不通的了,但其实,结论下的过早了,因为test()这个方法比较特殊,与类同名,是构造方法。

我们继续测试。

<?php
class test{

function test(){

echo ‘it works’;

}

function test2(){

echo ‘it works too’;

}

}

test::test2();

执行结果:

it works too

这说明,静态调用非静态方法是可行的,但是静态调用构造方法是不可以的。为了验证这个结论,我又做了如下测试:

<?php
class test{

static function test(){

echo ‘it works’;

}

}

test::test();

执行的结果如下:

Fatal error: Constructor test::test() cannot be static in /home/xxx/test.php on line 9

构造方法不能声明静态,所以上面的推论正确。

但这个结果的确是很特殊的,因为可能只有PHP可以静态调用非静态方法,我用Java做了实验,如果静态调用非静态方法会报如下错误:

Cannot make a static reference to the non-static method showString() from the type HelloWorldApp

其他语言我没有一一尝试,但这已经足够来说明PHP的特殊之处,关于为什么PHP会有这样的情况我暂时没有找到相关说明。

2.静态调用非静态方法是否应该被应用?

那我们是不是可以使用这种方法来代替static方法呢? 首先在代码的可读性上来看,静态调用非静态方法当然是不被推荐的,这会让维护者产生疑惑。

接下来我们再做一些实验,来看一下静态调用非静态方法在效率上是否会有一定的优势。

<?php
class test{function test2(){}}

for($k=0; $k<10000; $k++)

{

test::test2();

}

上面代码在我这里的执行时间是18到28毫秒,我们再来测试标准的写法。

<?php
class test{static function test2(){}}

for($k=0; $k<10000; $k++)

{

test::test2();

}

上面的代码执行时间在5到10毫秒之间,这样看来,静态调用非静态方法的效率要比标准的静态方法调用低的多,所以在效率上也不推荐静态调用非静态方法

url重写后的路径问题

Friday, October 17th, 2008

以前用FleaPHP的url_rewrite模式一直有一个很头痛的问题, 就是页面上相关资源的路径问题.

问题大概是这样的, 为了让url更加友好我们使用apache的mod_rewrite将类似

http://localhost/?controller=default&action=index&a=1

这样的路径重写成

http://localhost/default/index/a/1

或者

http://localhost/default/index/a/1.html

这样, 但是如果是这样那么浏览器请求页面相关资源的基地址就发生了改变, 从原来的documentroot下变成了documentroot/default/index/a, 如果你要在页面上显示一个图片 1.jpg, 那么原来本来应该在documentroot/1.jpg下找到的文件却变成了documentroot/default/index/a/1.jpg, 那结果肯定是找不到的.

为了解决这个问题以前用过很多方法, 用的最多的就是使用绝对路径来解决这个问题, 这是有一个前提的, 就是程序一定要放在虚拟机的根目录下, 这样在html中要将所有的资源都是用/开头, /的意思是从根目录下开始寻找, 在localhost下的任何一个程序在任何目录中使用/1.jpg都会被指向documentroot/1.jpg, 这里再强调一个概念, “./”是当前目录, “../”是上级目录, “/”是根目录.

上面这个方法实际上已经可以解决这个问题了, 但是有局限, 就是程序的目录是固定的, 一定要在虚拟机根目录下, 这样或多或少会带来一些麻烦, 至少我觉得调试不方便. 于是又有了第二个解决方案, 也就是上面方案的升级版. 我们在全局定义一个常量 define(‘PRE_DIR’, ‘/’); 这样的话要将程序中所有的资源路径都写成类似 <?php echo PRE_DIR;?>dir/filename.jpg, 这样的话资源会被定为到 /dir/filename.jpg, 如果要将程序放在二级目录 test/下的话, 只要将PRE_DIR的值改成’/test/’就可以了.

其实在这之后又有了升级版, 但是不打算介绍太多, 因为这不是重点, 在手动改目录之后大家就想到要程序自动检测当前目录, 这个以前有做过, 是个不错的想法, 代码不少, 但是找不到了.

还有个东西我觉得比较神奇的, smarty有一个自动替换的插件, 可以自动在所有模板的资源链接前面加前缀, 当然前提是你已经定义了这个前缀, 插件名不记得了, 太久不用找不到了.

还是说重点吧, 昨天bobhero给我提供了一个新的思路, 利用mod_rewrite重新对资源进行定向, 后来到网上找相关的资料, 没有找到, 只找到了一些mod_rewrite的教程什么的. 研究了一下午也没研究出来, 因为url太复杂了, 很多情况都要考虑进去, 正则说什么就写不明白了, 今天早上终于搞定了.

_________.htaccess_____________________

RewriteEngine on

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^.]*)(\.html)?$ index.php/$1 [L]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^/]*)/(.*\..*)$ $2

________end of .htaccess_____________

起作用的其实就这些东西,  可能还会有错误, 不过的确生效了.简单解释一下, RewriteCond %{REQUEST_FILENAME} !-f是条件语句, 意思是说如果请求的文件不存在下面的规则才生效. 第一条规则RewriteRule ^([^.]*)(\.html)?$ index.php/$1 [L]是FLEAPHP的重写规则.

下面两条才是为了解决路径问题的, RewriteRule ^([^/]*)/(.*\..*)$ $2的作用大概是这样, 比如url是这样controller/action/page/1,那么该页的style/css.css可能变成了controller/action/page/style/css.css, 这条语句就是要将controller/action/page/style/css.css指向action/page/style/css.css, 而由于某种机制请求每更换一次目录就会再执行一次根目录下的.htaccess中的重写规则, 由于action/page/style/css.css也不存在所以又被定向到page/style/css.css, 同理page/style/css.css也不存在, 再向上 style/css.css, 由于文件存在而不再执行重写规则, 向用户返回style/css.css的内容.

总的来说最后的这个方法除了apache受点累还是很完美的, 这样大概可能每个资源请求都会被重定向4次以上, 目前还不清楚对执行效率有多大的影响.

参考资料:

竹笋炒肉:Apache的Mod_rewrite学习

使用PHP收发邮件

Tuesday, September 9th, 2008

这两天都在测试邮件, 搞得头都大了, 现在技术问题都已经解决了, 记录一下.

发送邮件一直都是一个难题, 以前没用过mail函数, 因为多数的环境下都不支持, 曾经改写过Discuz的邮件类, 也用过像PHPMailer这样的邮件发送库来解决邮件发送的问题, 但实际上用的都是SMTP. 昨天bobhero在公司内网服务器上配置了sendmail, 简单配置了之后可以使用smtp, imap和pop3. 但按照网上的教程配置了之后php的mail()函数一直都无法发送邮件, 在搜索引擎上寻找很久没有结果, 最后bobhero重新编译了配置文件, 终于成功.

编译命令

make -C /etc/mail

收邮件要比发邮件复杂的多, 网上资源巨少, 好不容易有几篇英文的帖子和我情况类似也没有解决办法.

接受邮件使用php的imap库, 遇到的问题主要在imap_open()上

连接163使用

imap_open("{pop3.163.com:110/pop3}INBOX", $username, $password);

取得的7bit数据使用imap_qprint()解码.

连接gmail使用

imap_open("imap.gmail.com:993/imap/ssl", $username, $password);

最难弄的就是连接本地的imap服务器, Outlook是可以从服务器上收取邮件的, 但是php就是收不到并返回错误Certificate failure for 102.com: Self-signed certificate or untrusted authority: OU=IMAP server, CN=imap.example.com, E=postmaster@example.com, 使用telnet链接也没有问题, 从昨天下午研究到今天中午也没解决这个错误, 网上搜不到解决办法, 最后实在没办法了, 用IP Sniffer截php.exe的数据包, 然后用telnet进行模拟, 发现php.exe在和imap服务器链接成功之后使用了TLS(一个什么传输安全协议), 刚想放弃就在php.net上看到原来imap_open()还有一个参数是/notls, 我的天, 终于得救了.

imap_open("{localhost:143/imap/notls}INBOX", $username, $password);

研究期间发现telnet很好玩, 可以用telnet检查某项服务是否开启, 比如smtp, telnet 192.168.1.102 25, 如果服务是开启的就会建立一个连接, 可以用指令进行smtp 的相关操作, 而指令内容也就是smtp协议的相关内容, 其他协议pop, imap等等都可以这么玩.

相关端口:

smtp 协议 25端口

imap 协议 143端口

imap/ssl 993端口

imap/ssl/novalidate-cert 995端口

nntp协议 119端口

相关资源:

中国协议分析网 http://www.cnpaf.net/

RFC文档 http://www.faqs.org/rfcs/

保留用户输入的错误数据

Saturday, September 6th, 2008

最近公司项目比较闲, 所以很多时间都在整理代码, 这几天就又有了一个这样的需求, 当用户提交了错误的信息, 提示错误并保留用户输入的数据。

起初第一个想法就是使用COOKIE, 后来又考虑使用$_SESSION, 其实两者无明显区别,大概流程是这样的, 每个请求的前面都加入下面的逻辑:

if(有保存的数据){

    if(数据是不是本动作产生的){

        如果是则取得数据

    }

    清空保存的数据

}

if(是一个POST请求){

    保存用户的请求数据

}

这样处理的话消息处理需要中间页跳转,就是说错误提示是一个单独的页面,这样基本可以做到将数据保存到下一个请求。这个方法后来在显示上比较麻烦,就是需要判断有没有取得的数据,有的话输出没有的话不输出, 如果使用模板引擎的话处理起来更加复杂, 于是又提出了另一种解决方案。

将表单和处理程序放在一起(可能是一个function , MVC中应该是一个action中),这样的话需要显示表单或者处理数据要准备一套结构相同的数据传给页面,然后由本页显示错误消息, 不需要使用COOKIE.

function actionCreate(){

    if(是POST请求){

        过滤数据

        验证数据并生成错误

        if(没有错误){

            向数据库插入数据

        }else{

            将错误数据提交给显示层

        }

    }else if(是GET请求并且必要参数正确){

        从数据库取得显示数据提交给显示层

    }else{
        返回一个参数错误
    }

    $this->display('create.html');

}

可以说这个方法是比较完美的, 目前来说没有什么不足之处, 但是有很多细节是需要注意的, 比如form的action要提交给自身并且携带参数, 虽然参数无作用, 但可以用来刷新页面(window.location.href=window.location.href), 显示层刷新页面的功能是必须的, 因为如果是修改操作那么输入错误之后将会保留错误的信息。

接下来还可以通过控制错误消息的结构识别错误是由哪一个表单项产生的, 如果需要还可以对错误进行自动修改等等。

Ext学习笔记(1

Thursday, August 7th, 2008

昨天用了一下午学习Ext, 搞了一下午最后连个表格都没搞出来, 今天继续, 顺便记录一下.

中文在线手册 http://www.ajaxjs.com/docs/

这里请大家注意一下, 看手册一定要看源码包里的那个docs目录里的, Ext手册版本差异很大, 不一定最新的就一定适用。

使用Ext之前需要加载Ext库, 昨天直接加载核心, 竟然不好用, 最后看例子里面是加载了两个文件, 原因还没搞清楚, 但先记下来.

<script type=”text/javascript” src=”../ext-2.2/adapter/ext/ext-base.js”></script>
<script type=”text/javascript” src=”../ext-2.2/ext-all.js”></script>

最基础的

Ext.onReady(fn);

这个和Jquery的$(fn)是一样的, 就是页面加载完毕执行fn

Ext.get(‘idName’);

这个就是Jquery中的$(‘#idName’), 返回一个Ext的Element对象.

Element.dom是该对象的DOM节点, 其他内容看手册里的Element部分

Ext.select(‘p’)

这个类似Jquery中的$(‘p’), 但是Ext中没有each, Ext.select(‘p’).methodName()直接就作用在全部对象上.

事件绑定

Element.on(‘click’, fn);

获得事件句柄

clicked = function(e){

Ext.get(e.target).highlight();//e.target为触发该事件的对象id, highlight是高亮闪烁

}

Ext.select(‘p’).on(‘click’,clicked);

消息框

Ext.MessageBox为Ext的消息对象.

Ajax

ajax部分和Jquery非常相像了

Ext.get(‘msg’).load({
url: ‘http://test/ext/data.php’,
params: ‘name=1′,
text: ‘Updating…’
});