两个PHP代码片段

最近有同事碰到两个问题:

这段代码输出什么?

这段呢?

第一个代码输出true,原因在于in_array使用了==的逻辑(如果第三个参数为true则为===的逻辑),不知道这样是否合理。源码中php将字符串转成了数字,转不了则返回0。(以上测试版本为5.5.30和7.0.0)

第二个代码输出:

因为第一次使用了引用,引用计数不释放,引用的地址是数组中c的地址。第二次循环对$m赋值的时候,不停的修改了$m指向的地址,数组中的地址也就发生了变化。赋值的源码位在于zend_execute.c/zend_assign_to_variable方法中。

PHP访问类私有元素

从PHP5开始,我们通过反射类来访问私有方法:

继续阅读

php使用反射访问私有元素

php的反射机制可以让使用者获取到一些元素的运行信息、状态等,我们可以通过ReflectionClass导出类、访问其元素、获取类定义信息等,可以通过ReflectionFunctionReflectionMethodReflectionProperty等类访问函数、方法、属性之类元素的信息。

下面是一个可以访问私有元素的类,通过ReflectionMethodReflectionClass实现:

反射类比较强大,用类的方法实现也更加直观。为我们进行一些简单测试或者工具制作上提供了不小的便利。

几个语言中的迭代器

上次有朋友说python的迭代器很好用,就是不好理解,我说其实php也有迭代器的,只是很多人不知道而已。

python迭代器:

先说说python的迭代器,python的迭代器使用yield关键字来实现,可以被for调用。

还是来拿读文件来说事,我们有一个文件,里面的数据都是TAB分隔,如果我们想实现一个一行行读取的库核心的地方可以使用迭代器来写:

tab文件(html输出后估计tab变成了空格,复制的同学请注意):

迭代器代码:

这里涉及迭代器的部分在于read方法中的yield迭代器,虽然是for循环,在yield调用一次以后会挂起等待下一次调用yield,所以很多人在这里需要理解很久。所以yield在while内自然也是一样的效果。

继续阅读

php的traits和单例

最近和同事聊天时发现很多同事并不了解php的一些特性。有时他们会说python的某些特性非常有趣,确实,但我提到php中也存在一些类似特性的时候,他们表示并不了解,于是考虑写几篇关于php特性的文章。如果其他语言有这类特性,我会尝试着对比来讲解。

phper们应该都知道在5.4以后增加了一个新特性叫做traits,其文档如下:

http://php.net/manual/zh/language.oop5.traits.php

traits感觉像是让你更方便的复制粘贴代码,这些代码统一的管理着。你可以使用trait_exists方法来检查traits是否存在,使用class_uses列出所有使用traits的方法。

网上有些文章吐槽traits这个特性破坏了封装或是如何,我的层次不高,只能默默表示有时候用这东西还是比较舒服的。

比如我们写单例方法,这东西我们的项目中经常性的用到:

为了不让别人初始化,我们需要控制构造函数好克隆方法的权限,然后让类自己检查和初始化自己。

继续阅读

php 异步执行脚本

最近有个地方需要拉起一个脚本处理任务,起了进程交给init进程发现还是会阻塞,后来找到这篇文章,发现原来自己想的太复杂。

这里说的异步执行是让php脚本在后台挂起一个执行具体操作的脚本,主脚本退出后,挂起的脚本还能继续执行。比如执行某些耗时操作或可以并行执行的操作,可以采用php异步执行的方式。主脚本和子脚本的通讯可以采用外部文件或memcached的方式。原理就是通过exec或system来执行一个外部命令。注意:本文所述的是针对Linux环境。

在Linux下要让一个脚本挂在后台执行可以在命令的结尾加上一个 “&” 符号,有时候这还不够,需要借助nohup命令才能是忽略挂起信号。

继续阅读

PHP autoload和spl_autoload自动加载机制详解 [转]

PHP autoload机制详解

(1) autoload机制概述

在使用PHP的OO模式开发系统时,通常大家习惯上将每个类的实现都存放在一个单独的文件里,这样会很容易实现对类进行复用,同时将来维护时也很便利。这 也是OO设计的基本思想之一。在PHP5之前,如果需要使用一个类,只需要直接使用include/require将其包含进来即可。下面是一个实际的例 子:

在这个例子中,no-autoload.php文件需要使用Person类,它使用了require_once将其包含,然后就可以直接使用Person类来实例化一个对象。

但 随着项目规模的不断扩大,使用这种方式会带来一些隐含的问题:如果一个PHP文件需要使用很多其它类,那么就需要很多的require/include语 句,这样有可能会造成遗漏或者包含进不必要的类文件。如果大量的文件都需要使用其它的类,那么要保证每个文件都包含正确的类文件肯定是一个噩梦。

PHP5为这个问题提供了一个解决方案,这就是类的自动装载(autoload)机制。autoload机制可以使得PHP程序有可能在使用类时才自动包含类文件,而不是一开始就将所有的类文件include进来,这种机制也称为lazyloading。 下面是使用autoload机制加载Person类的例子:

通常PHP5在使用一个类时,如果发现这个类没有加载,就会自动运行__autoload()函数,在这个函数中我们可以加载需要使用的类。在我们这个简 单的例子中,我们直接将类名加上扩展名" title="扩展名">扩展名”.class.php”构成了类文件名,然后使用require_once将其加载。

从这个例子中,我们可以看出 autoload至少要做三件事情,第一件事是根据类名确定类文件名,第二件事是确定类文件所在的磁盘路径(在我们的例子是最简单的情况,类与调用它们的 PHP程序文件在同一个文件夹下),第三件事是将类从磁盘文件中加载到系统中。第三步最简单,只需要使用include/require即可。要实现第一 步,第二步的功能,必须在开发时约定类名与磁盘文件的映射方法,只有这样我们才能根据类名找到它对应的磁盘文件。 继续阅读

PHP的Phar简介

php的phar扩展是参考java的jar设计出来的功能,平时在使用的时候似乎比较少,但是如果我们想像java一样把自己的类库打个包发布,phar是个不错的选择。

这里对Phar做个简单的介绍。

phar和jar不太相同的是不需要通过第三方工具去支持,不过phar必须在5.2.0+上使用,php在5.3.0增加了phar扩展原生支持Phar,使得效率提升很多。

在所有工作之前我们需要修改php.ini才能打包phar文件,修改如下项:

创建之前,我们先创建如下目录结构:

文件内容如下:

我们先创建个Phar包试试:

我们建立一个叫做test.phar的包,设置其Meta信息,通过使用buildFromDirectory方法把test.dir目录里的文件给打包进去。最后我们把test.dir目录中的index.php设置问默认的访问文件(stub)。这样我们每次include这个test.phar的时候都会执行index.php。

让我们使用一下打包后的phar吧:

我们解开包看一下:

生成了一个test.phar.zip文件,我们解压看一下:

我们发现就是我们打包的文件夹的内容。

好了,暂时就介绍到这里,想深一步了解请参考php文档

PHP的自动加载在CLI模式下是否可用

PHP使用__autoload()或者spl_autoload_register() 的时候,有些人会好奇这是否在CLI模式下使用。

PHP手册“自动加载对象”这章(手册地址)有一句Note内容如下:

自动加载不可用于 PHP 的 CLI 交互模式。

所以,自动加载方法CLI模式下是可用的,前提是你使用的不是交互模式(php -a)。

ps.章节里写到建议使用更加灵活的spl_autoload_register()代替__autoload。__autoload在以后的版本或许会被弃用。所以魔术方法并不一定都是优先使用的。

Module 'ModuleName' already loaded in Unknow on line 0问题的解决

编译扩展的时候发现类似这样的提示:

Module 'ModuleName' already loaded in Unknow on line 0

这是因为扩展已经加载过一次,而使用者有一次的把扩展信息写入了php.ini,一般编译进php的扩展都是自动加载的,不需要额外写。

如果你使用php -m发现去掉以后扩展没有加载进去,那么不要删除php.ini扩展信息,这很有可能是你在编译的时候设置了查找php.ini查找路径(--wth-config-file-scan-dir)和php.ini(--with-config-file-path)的路径,而且恰好是同一目录,导致加载php.ini两次,所以报出上面的错误。