搜索
首页php教程php手册用php实现一个简单的链式操作

最近在读《php核心技术与最佳实践》这本书,书中第一章提到用__call()方法可以实现一个简单的字符串链式操作,比如,下面这个过滤字符串然后再求长度的操作,一般要这么写:

<span style="color: #008080;">strlen</span>(<span style="color: #008080;">trim</span>(<span style="color: #800080;">$str</span>));

那么能否实现下面这种写法呢?

<span style="color: #800080;">$str</span>-><span style="color: #008080;">trim</span>()-><span style="color: #008080;">strlen</span>();

下面就来试下。

链式操作,说白了其实就是链式的调用对象的方法。既然要实现字符串的链式操作,那么就要实现一个字符串类,然后对这个类的对象进行调用操作。我对字符串类的期望如下:(1)当我创建对象时,我可以将字符串赋值给对象的属性,并且可以访问这个属性读取值;(2)我可以调用trim() 和strlen()方法;(3)我还可以这么调用方法$str->trim()->strlen()。

上面的第(1)条,是一个字符串类的基本要求。先把这个实现了:

<span style="color: #008080;">1</span> <span style="color: #0000ff;">class</span> <span style="color: #0000ff;">String</span>
<span style="color: #008080;">2</span> <span style="color: #000000;">{
</span><span style="color: #008080;">3</span>     <span style="color: #0000ff;">public</span> <span style="color: #800080;">$value</span><span style="color: #000000;">;
</span><span style="color: #008080;">4</span> 
<span style="color: #008080;">5</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __construct(<span style="color: #800080;">$str</span>=<span style="color: #0000ff;">null</span><span style="color: #000000;">)
</span><span style="color: #008080;">6</span> <span style="color: #000000;">    {
</span><span style="color: #008080;">7</span>         <span style="color: #800080;">$this</span>->value = <span style="color: #800080;">$str</span><span style="color: #000000;">;
</span><span style="color: #008080;">8</span> <span style="color: #000000;">    }
</span><span style="color: #008080;">9</span> }

可以试下:

<span style="color: #008080;">1</span> <span style="color: #800080;">$str</span> = <span style="color: #0000ff;">new</span> <span style="color: #0000ff;">String</span>('01389'<span style="color: #000000;">);
</span><span style="color: #008080;">2</span> <span style="color: #0000ff;">echo</span> <span style="color: #800080;">$str</span>->value;

然后再看第2条,先把$str->trim()实现了,参考书中的思路:触发__call方法然后执行call_user_func。代码如下:

<span style="color: #008080;"> 1</span> <span style="color: #0000ff;">class</span> <span style="color: #0000ff;">String</span>
<span style="color: #008080;"> 2</span> <span style="color: #000000;">{
</span><span style="color: #008080;"> 3</span>     <span style="color: #0000ff;">public</span> <span style="color: #800080;">$value</span><span style="color: #000000;">;
</span><span style="color: #008080;"> 4</span> 
<span style="color: #008080;"> 5</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __construct(<span style="color: #800080;">$str</span>=<span style="color: #0000ff;">null</span><span style="color: #000000;">)
</span><span style="color: #008080;"> 6</span> <span style="color: #000000;">    {
</span><span style="color: #008080;"> 7</span>         <span style="color: #800080;">$this</span>->value = <span style="color: #800080;">$str</span><span style="color: #000000;">;
</span><span style="color: #008080;"> 8</span> <span style="color: #000000;">    }
</span><span style="color: #008080;"> 9</span> 
<span style="color: #008080;">10</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __call(<span style="color: #800080;">$name</span>, <span style="color: #800080;">$args</span><span style="color: #000000;">)
</span><span style="color: #008080;">11</span> <span style="color: #000000;">    {
</span><span style="color: #008080;">12</span>         <span style="color: #800080;">$this</span>->value = <span style="color: #008080;">call_user_func</span>(<span style="color: #800080;">$name</span>, <span style="color: #800080;">$this</span>->value, <span style="color: #800080;">$args</span>[0<span style="color: #000000;">]);
</span><span style="color: #008080;">13</span>         <span style="color: #0000ff;">return</span> <span style="color: #800080;">$this</span><span style="color: #000000;">;
</span><span style="color: #008080;">14</span> <span style="color: #000000;">    }
</span><span style="color: #008080;">15</span> }

测试下:

<span style="color: #008080;">1</span> <span style="color: #800080;">$str</span> = <span style="color: #0000ff;">new</span> <span style="color: #0000ff;">String</span>('01389'<span style="color: #000000;">);
</span><span style="color: #008080;">2</span> <span style="color: #0000ff;">echo</span> <span style="color: #800080;">$str</span>-><span style="color: #008080;">trim</span>('0')->value;

结果如下:

上面需要注意的是第12行: $this->value = call_user_func($name, $this->value, $args[0]); $name是回调函数的名字(这里也就是trim),后面两个是回调函数(tirm)的参数,参数的顺序不要弄颠倒了。$args是数组,也需要注意下。

第2条中还要实现strlen(),这时上面代码中的第13行就很关键了: return $this; 它的作用就是,在第12行调用trim()处理完字符串后重新value属性赋值,然后返回当前对象的引用,这样对象内的其他方法就可以对属性value进行连续操作了,也就实现了链式操作。$str->strlen()实现如下:

<span style="color: #008080;"> 1</span> <span style="color: #0000ff;">class</span> <span style="color: #0000ff;">String</span>
<span style="color: #008080;"> 2</span> <span style="color: #000000;">{
</span><span style="color: #008080;"> 3</span>     <span style="color: #0000ff;">public</span> <span style="color: #800080;">$value</span><span style="color: #000000;">;
</span><span style="color: #008080;"> 4</span> 
<span style="color: #008080;"> 5</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __construct(<span style="color: #800080;">$str</span>=<span style="color: #0000ff;">null</span><span style="color: #000000;">)
</span><span style="color: #008080;"> 6</span> <span style="color: #000000;">    {
</span><span style="color: #008080;"> 7</span>         <span style="color: #800080;">$this</span>->value = <span style="color: #800080;">$str</span><span style="color: #000000;">;
</span><span style="color: #008080;"> 8</span> <span style="color: #000000;">    }
</span><span style="color: #008080;"> 9</span> 
<span style="color: #008080;">10</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __call(<span style="color: #800080;">$name</span>, <span style="color: #800080;">$args</span><span style="color: #000000;">)
</span><span style="color: #008080;">11</span> <span style="color: #000000;">    {
</span><span style="color: #008080;">12</span>         <span style="color: #800080;">$this</span>->value = <span style="color: #008080;">call_user_func</span>(<span style="color: #800080;">$name</span>, <span style="color: #800080;">$this</span>->value, <span style="color: #800080;">$args</span>[0<span style="color: #000000;">]);
</span><span style="color: #008080;">13</span>         <span style="color: #0000ff;">return</span> <span style="color: #800080;">$this</span><span style="color: #000000;">;
</span><span style="color: #008080;">14</span> <span style="color: #000000;">    }
</span><span style="color: #008080;">15</span> 
<span style="color: #008080;">16</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> <span style="color: #008080;">strlen</span><span style="color: #000000;">()
</span><span style="color: #008080;">17</span> <span style="color: #000000;">    {
</span><span style="color: #008080;">18</span>         <span style="color: #0000ff;">return</span> <span style="color: #008080;">strlen</span>(<span style="color: #800080;">$this</span>-><span style="color: #000000;">value);
</span><span style="color: #008080;">19</span> <span style="color: #000000;">    }
</span><span style="color: #008080;">20</span> }

测试下:

<span style="color: #008080;">1</span> <span style="color: #800080;">$str</span> = <span style="color: #0000ff;">new</span> <span style="color: #0000ff;">String</span>('01389'<span style="color: #000000;">);
</span><span style="color: #008080;">2</span> <span style="color: #0000ff;">echo</span> <span style="color: #800080;">$str</span>-><span style="color: #008080;">strlen</span>();

结果:

链式操作:

<span style="color: #0000ff;">echo</span> <span style="color: #800080;">$str</span>-><span style="color: #008080;">trim</span>('0')-><span style="color: #008080;">strlen</span>();

结果:

 


到这里,这篇文章本该就结束了。但是,我想了下,其实不用__call()方法,也是可以实现链式操作的。下面是不用__call()的实现:

<span style="color: #008080;"> 1</span> <span style="color: #0000ff;">class</span> <span style="color: #0000ff;">String</span>
<span style="color: #008080;"> 2</span> <span style="color: #000000;">{
</span><span style="color: #008080;"> 3</span>     <span style="color: #0000ff;">public</span> <span style="color: #800080;">$value</span><span style="color: #000000;">;
</span><span style="color: #008080;"> 4</span> 
<span style="color: #008080;"> 5</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __construct(<span style="color: #800080;">$str</span>=<span style="color: #0000ff;">null</span><span style="color: #000000;">)
</span><span style="color: #008080;"> 6</span> <span style="color: #000000;">    {
</span><span style="color: #008080;"> 7</span>         <span style="color: #800080;">$this</span>->value = <span style="color: #800080;">$str</span><span style="color: #000000;">;
</span><span style="color: #008080;"> 8</span> <span style="color: #000000;">    }
</span><span style="color: #008080;"> 9</span> 
<span style="color: #008080;">10</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> <span style="color: #008080;">trim</span>(<span style="color: #800080;">$t</span><span style="color: #000000;">)
</span><span style="color: #008080;">11</span> <span style="color: #000000;">    {
</span><span style="color: #008080;">12</span>         <span style="color: #800080;">$this</span>->value = <span style="color: #008080;">trim</span>(<span style="color: #800080;">$this</span>->value, <span style="color: #800080;">$t</span><span style="color: #000000;">);
</span><span style="color: #008080;">13</span>         <span style="color: #0000ff;">return</span> <span style="color: #800080;">$this</span><span style="color: #000000;">;
</span><span style="color: #008080;">14</span> <span style="color: #000000;">    }
</span><span style="color: #008080;">15</span> 
<span style="color: #008080;">16</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> <span style="color: #008080;">strlen</span><span style="color: #000000;">()
</span><span style="color: #008080;">17</span> <span style="color: #000000;">    {
</span><span style="color: #008080;">18</span>         <span style="color: #0000ff;">return</span> <span style="color: #008080;">strlen</span>(<span style="color: #800080;">$this</span>-><span style="color: #000000;">value);
</span><span style="color: #008080;">19</span> <span style="color: #000000;">    }
</span><span style="color: #008080;">20</span> }

链式操作的关键是在做完操作后要return $this。

另外,本文受到园子里这篇文章的启发,用call_user_func_array()替换了call_user_func()实现,将__call()方法修改如下。

<span style="color: #008080;">1</span>     <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __call(<span style="color: #800080;">$name</span>, <span style="color: #800080;">$args</span><span style="color: #000000;">)
</span><span style="color: #008080;">2</span> <span style="color: #000000;">    {
</span><span style="color: #008080;">3</span>         <span style="color: #008080;">array_unshift</span>(<span style="color: #800080;">$args</span>, <span style="color: #800080;">$this</span>-><span style="color: #000000;">value);
</span><span style="color: #008080;">4</span>         <span style="color: #800080;">$this</span>->value = <span style="color: #008080;">call_user_func_array</span>(<span style="color: #800080;">$name</span>, <span style="color: #800080;">$args</span><span style="color: #000000;">);
</span><span style="color: #008080;">5</span>         <span style="color: #0000ff;">return</span> <span style="color: #800080;">$this</span><span style="color: #000000;">;
</span><span style="color: #008080;">6</span>     }

与上面的__call()方法效果是相同的,这样代码似乎比之前的实现要优雅些。


总结:

__call()在对象调用一个不可访问的方法时会被触发,所以可以实现类的动态方法的创建,实现php的方法重载功能,但它其实是一个语法糖(__construct()方法也是)。

那么如果没有__call()等语法糖,能否实现动态方法的创建和链式操作呢?我想会涉及到以下几个方面的问题:类方法是否存在和可以调用,这个可以用method_exists、is_callable、get_class_methods等方法来实现,另外,就是在创建对象时给属性赋值(初始化),这个语法糖确实方便,不过不是必需的。等有时间再研究下吧。

 

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

Dreamweaver Mac版

Dreamweaver Mac版

视觉化网页开发工具

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )专业的PHP集成开发工具