搜索
首页后端开发php教程PHP中原生类型的方法

引言

第一次,翻译别人的文章,用四级英语的水平来翻译~~囧,可能有很多不太恰当的地方,尽管拍砖(有些地方实在想不到恰当的翻译,我同时贴出了原文和自己很low的翻译)。

翻译这篇文章用了我3个晚上一个中午~,先弄明白技术上大体再说什么,然后在翻译的~

这样做的目的一方面锻炼下自己的英文,一方面学习点国外的比较新的技术想法。

这篇文章主要讲了对PHP中的原生类型实现面向对象的操作,通过扩展的方式实现,用来解决PHP中函数命名不规范、参数顺序不规范、可读性低的问题。

扩展的实现是通过改变ZEND引擎中调用面向对象方法时对应的处理函数来实现的,注册一个函数,判断面向对象调用者的类型,如果是IS_STRING则继续自定义处理,否则返回ZEND默认的处理函数,看看下面的解释

HOOKPHP代码

PHP是解释型语言,代码被翻译为中间字节码由ZEND引擎解析执行。PHP把中间字节码称之为OPCODE,每个OPCODE对应ZEND底层的一个处理函数,ZEND引擎最终执行这个处理函数。实现HOOK功能只需要改变HOOK OPCODE对应的处理函数即可。

PHP中原生类型的面向对象的方法

几天前,Anthony Ferrara写下了一些对PHP未来的看法。我同意他的大部分观点,但不是所有的。这篇文章关注的是一个特别的方面:将像字符串、数组这样的原生类型伪装为“伪对象”,可以在这些原生类型上执行方法调用。

让我们从几个例子开始看看为什么需要“伪对象”:

<code>$str = "test foo bar";
$str->length();      // == strlen($str)        == 12
$str->indexOf("foo") // == strpos($str, "foo") == 5
$str->split(" ")     // == explode(" ", $str)  == ["test", "foo", "bar"]
$str->slice(4, 3)    // == substr($str, 4, 3)  == "foo"

$array = ["test", "foo", "bar"];
$array->length()       // == count($array)             == 3
$array->join(" ")      // == implode(" ", $array)      == "test foo bar"
$array->slice(1, 2)    // == array_slice($array, 1, 2) == ["foo", "bar"]
$array->flip()         // == array_flip($array)        == ["test" => 0, "foo" => 1, "bar" => 2]</code>

这里的$str仅仅是一个普通的字符串,$array仅仅是一个普通的数组——他们不是对象。我们所做的仅仅是给他们了一点儿比较像对象的特性,让他们可以调用方法。

注意上面的特性可不是遥不可及,而是现在已经存在了。PHP扩展scalar_objects允许你在原生PHP类型上面定义面向对象的方法。

原生类型引入对象式的调用方法也带来了许多好处,我会在下面列出来:

一个让API进一步让简洁的机会

听到最多的最常见的关于PHP的抱怨很可能是标准库中函数的前后矛盾、命名不清楚,还有前后不一致、顺序混乱的参数。一些典型的例子:

<code>//不同的函数命名约定
strpos
str_replace

//很不清晰的命名
strcspn
strpbrk

//颠倒的参数顺序
strpos($haystack, $needle)
array_search($needle, $haystack)</code>

然而这些问题通常被过分强调了(我们有集成开发环境),很难否定状况还是相当好的。还应当指出的是,许多函数表现出的问题远远超过了诡异的名字这个问题。通常边缘情况的行为是没有被完全考虑的,因而有了在调用代码中对它们进行特殊处理的需要。(对字符串函数来说,边缘情况通常包括空字符串或者超出字符串范围的偏移量。)

一个一般的建议是在PHP6中实现巨大数量的别名,用来统一函数名称和参数顺序。因此我们将会有string\pos(),string\reoplace(),string\complement_span()或者类似的。个人而言(而且这看起来像是对许多php-src开发者的意见)这些对我的意义很小。现在的这些函数名已经深深的根植在PHP程序员的记忆中了,对它们实现一些不重要的装饰性的改变看起来好像不值得。

原生类型面向对象API的引入另一方面也提供了一个API重新设计的契机(原文:The introduction of an OO API for primitive types on the other hand offers an opportunity of an API redesign as a side effect of switching to a new paradigm)。这也是真正让我们从零开始,不需要考虑旧的API的期望输出。两个例子:

  • 我非常希望有$string->split($delimiter)$array->join($delimiter)这样的方法,这些是函数功能是普遍接受的名字(与explodeimplode不同)。另一方面如果有一个string\split($delimiter)方法有这样的行为我会感到非常反感,因为已经存在的str_split函数所做的是完全不同的(分组)。
  • 我理所当然的喜欢新的对错误报告使用异常的API,由于这是一个面向对象的API,那是自动给定的,异常当然也是和重命名的API一起使用,然而这违背了现在所有的程序函数对错误处理使用警告的惯例,这不是一成不变的,但这确实是我想避免的一个争议点:)

这是我实现原生类型面向对象API的主要动机:从零开始,允许我们实现一套合理的API设计。当然了,这个改动的所有好处不知这些。面向对象的语法提供了许多更深层的好处,将在下面讨论。

提高了可读性

程序式的调用一般没有链式调用好。考虑下面的例子:

<code>$output = array_map(function($value) {
    return $value * 42;
}, array_filter($input, function($value) {
    return $value > 10;
});</code>

咋一看,哪个是arraay_maparray_filter各自的使用?(原文:what are array_map and array_filter applied to? )他们调用的顺序是什么?变量$input隐藏在两个闭包之间,函数的书写顺序也和他们实际调用的顺序相反。现在同样的例子使用面向对象的语法:

<code>$output = $input->filter(function($value){
	return $value > 10;
})->map(function($value){
	return $value * 42;
});</code>

我敢说使用这种方式,操作的顺序(先filtermap)和初始输入数组$input更加明显。

这个例子明显有人为拼凑的感觉,因为array_maparray_filter是函数参数顺序颠倒的另外一个例子(这就是为什么输入数组在中间)。再看另外一个输入参数在同一个位置的例子(来自实际的代码):

<code>substr(strstr(rtrim($className, '-'), '\\', '_'), 15);</code>

在这个例子中,最后面是一连串的额外的参数'_'), '\\', '_'), 15,,很难把这些参数和应用的函数对应起来。把这个和使用面向对象方法的版本做个比较:

<code>$className->trimRight('_')->replace('\\', '_')->slice(15);</code>

这次函数运算和他们的参数紧密的联系在了一起,而且方法的调用和他们的执行顺序相匹配。

另一个来自这种语法的可读性的好处是needle/haystack不明确问题。别名通过引入统一的参数顺序规范让我们解决了这个问题,使用面向对象的API这个问题基本不存在了。

<code>$string->contains($otherString);
$string->contains($someValue);

$string->indexOf($otherString);
$string->indexOf($someValue);</code>

这里哪个部分应用了哪个规则的困惑不复存在了。

多态

目前PHP有提供Contable接口,这个接口可以通过类实现自定义的输出函数count($obj)。为什么需要这个?因为我们PHP的函数没有多态。然而我们方法中确实需要多态:

如果数组实现$array->count()作为一个方法,实际上代码是不会在意$array是不是一个数组的,他可以是其他任何类型的实现count()方法的对象,这基本上给了我们Countable的所有行为,~(原文:This basically gives us the same behavior as Countable, just without the engine hackery it requires.)

这也是一个很一般的解决方案。举个例子,你可以实现一个实现所有字符串类型方法的UnicodeString类,然后可以随便的使用正常的字符串和UnicodeStrings。好吧,至少这还是理论。这很明显局限于那些字符串方法的使用,而且调用级联操作的时候会返回错误(原文:This would obviously only work as long as the usage is limited to just the string methods, and would fail once the concatenation operator is employed)(运算符重载目前只支持内核中的类)。

我仍然有强大的信念希望这个清晰起来,同样应用在数组等上面。通过继承相同的接口,你可有一个和数组行为方式相同的SplFixedArray。(原文:you could have an SplFixedArray behave the same way as an array, by implementing the same interface.)

既然我们已经总结了这个方法的一些好处,让我们也来看看它的问题:

松散的类型

摘抄自Anthony发表的博客:

标量不是对象,但更重要的是他们不是任何类型。PHP依赖一个类型系统,字符串和数字是同一个。系统中许多的灵活性基于任何标量可以很容易的转换为其他标量。

更重要的是,由于松散的类型系统,你不可能在任何时候知道一个变量的类型是哪个。你可以说出你想要他是什么类型,但你不知道他内在的类型是什么。Even with casting or scalar type hinting it isn’t a perfect situation since there are cases where types can still change.

为了阐明这个问题,考虑下面的例子:

<code>$num = 123456789;
$sumOfDigits = array_sum(str_split($num));</code>

这里$num被作为一个字符串数字,被str_split切分后使用array_sum求和。现在试试同样效果的面向对象方法调用:

<code>$num = 123456789;
$sumOfDigits = $num->chunk()->sum();</code>

这里字符串的cheunk()方法被数字来调用。会发生什么??Anthony建议这样解决:

这意味着所有的标量运算将必然需要对应的标量类型。这将导致需要一个标量有所有的数学方法的对象模型,当然包括所有的字符串方法。真是一个噩梦。。。。。

就像引言中所说的那样,这绝不是一个可接受的解决方法。然而我想我们可以绝对侥幸的逃脱仅仅在那种情况的时候抛出一个错误(异常!)。为了解释为什么这种方法是可行的,让我们看看PHP可以拥有哪些类型。

PHP中的原生类型

除了对象之外,PHP有下面的变量类型:

<code>null
bool
int
float
string
array
resource</code>

现在,我们考虑下上面这些里面的哪些会需要面向对象的方法:我们首先去掉resource,然后在剩下的里面看。nullbool明显不需要面向对象的方法,除非你想进行像$bool->invert()这样无聊的转换。

绝大多数的数学函数使用面向对象的方法也不是很合适。考虑下面几个例子:

<code>log($n)		$n->log()
sqrt($n)	$n->sqrt()
acosh($n)	$n->acosh()</code>

我想你会同意数学函数可读性比函数符号的形式更好。当然存在少许的面向对象方法你可以适当的应用数字类型,比如说$num->format(10)读起来相当的不错。然而,关于这里,对于一个面向对象的数字API不是真正的需要,只有少量的函数你可能需要。(再者来说目前的数学API在命名方面没有太多的问题,而且数学操作相关的命名相当的标准。)

现在剩给我们的只有字符串和数组了,我们已经看到这两种类型有许多很棒的API。但关于松散类型的问题我们所有的必须要做的有哪些?下面是重要的几点:

我们经常性的把字符串视为数字的时候(例如来自HTTP或者DB),这样反过来是不对的:直接将数字作为字符串非常少见。举个栗子,下面的代码将让我们感到困惑:

<code>strpos(54321, 32, 1);</code>

这样将数字视为字符串是一个很怪异的操作,这种情况也ok啊,只需要强制转换一次就好了。使用原来的求和数字的例子:

<code>$num = 123456789;
$sumOfDigits = ((string) $num)->chunk()->sum();</code>

现在我们弄明白了,是的,我们确实想将数字视为字符串。对我来说,这样来处理想这样使用这种技术的地方是可以接受的。

对于数组情况就更简单了:他不会出现讲一个数组操作视为一个其他不是数组类型的操作。

另一方面可以通过标量类型提示改善这个问题(我完全认为在PHP所有的版本都存在——最令人尴尬的问题是现在仍然没有(原文:which I totally assume to be present in any PHP version this gets in - really embarrassing that we still don’t have them))。如果内类型提示string,你获取输入的字符串将会是一个字符串(即使传递给函数的不是——这取决于类型提示实现的具体内容)。

当然了,我并不是暗示这里没有一点问题。由于错误的函数设计,有时候可能会发生未知的类型潜入代码中,例如substr($str, strlen($str))将自作聪明的返回bool(false)而不是string(0) ""。(不过,这个问题仅有substr存在。面向对象的API不存在那个问题,所以你碰不到那个问题。)

对象传递语义

除了若类型的问题之外,还有原生类型伪方法的一个语义的问题:PHP中的对象和其他类型相比有不同的传递语义(某种程度上和引用类似)。如果我们允许字符串和数组进行面向对象的方法调用,他们看起来会和对象很像,那样的话有些人可能期望他们有对象作为参数的传递语义。这个问题在在字符串和数组中都存在:

<code>function change($arg) {
	echo $arg->length();	//$arg looks like object
	$arg[0] = 'x';			//但是没有对象的传递语义
}

$str = 'foo';
change($str);	//$str stays the same

$array = ['foo', 'o', 'o'];
change($array);	//$array stays the same</code>

我们当然将会改变传递语义。首先,在我看来通过值传递来传递像数组这种大的数据结构是一个相当low的想法,我更愿意他们像对象一样传递。然而,那将是一个相当大的突破性的向后兼容,并且那将不易于自动的重构(原文:However, that would be a pretty big backwards-compatibility break and one that’s not easy to refactor automatically)(至少我猜想是这样的。我没有做实验去探索这样一个改变带来的实际影响)。另一方面,对于字符串通过对象方式传递参数将是一个灾难,除非我们让字符串同一时间完全的不可变,放弃目前所有的局部变量的可变性(我个人发现非常的容易——去尝试改变一个Python字符串的一个字节)。

我不知道是否有好的方法去解决这个预期的问题,除了在我们的文档中强调字符串和数组在面向对象的方法中仅仅视作”伪对像“,不是真正的对象。

这个问题可以被扩展到其他的对象相关的特性。例如你可将会问像$string instanceof string这样的是否正确。我还没有确定整个事情的完整走向。也许严格坚持仅仅在面向对象的方法使用,然后强调他们不是真正的对象会好一点。然而也许支持面向对象系统的更深层次的特性也会好点。这个观点应该进一步的思考下。

目前的状态

总而言之,这个方法有许多的问题,但我不认为他们特别重要。同时这个提供了一个很好的机会为我们的基本类型引入简洁明了的APIs,提高代码执行操作时候的可读性(可写性)。

那么这个想法目前的状态是什么呢?从我收集的内容来看,内部的人们不是特别的反对这个做法,但更愿意重命名所有的函数。主要的没有推进这个的原因是API提议~

为了这个目的,我创建了scalar_objects扩展,作为一个PHP扩展实现了这个功能。它允许你注册一个处理各自的原生类型的方法调用的类。看一个例子:

<code>class StringHandler {
	public function length(){
		return strlen($this);
	}

	public function contains($str){
		return false !== strpos($this, $str);
	}
}

register_primitive_type_handler('string', 'StringHandler');

$str = 'foo bar baz';
var_dump($str->legth());			//int(11)
var_dump($str->contains('bar'));	//bool(true)
var_dump($str->contains('hello'));	//bool(false)</code>

不久前,我开始了一个string handler包括一个API说明的工作,但一直没有真正的完成哪个项目(我希望我在不久找到一些重新开始他的动机)。当然也有许多其他项目在为实现这样的APIs而努力。

嗯,这是我想在PHP6中所看到的其中一个改进。我也许会为我的那个方向的计划写另外一篇文章。

引用

原文链接 : http://nikic.github.io/2014/03/14/Methods-on-primitive-types-in-PHP.html

HOOKPHP : http://netsecurity.51cto.com/art/201407/446430.htm

以上就介绍了PHP中原生类型的方法,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
如何在PHP中设置会话cookie参数?如何在PHP中设置会话cookie参数?Apr 22, 2025 pm 05:33 PM

在PHP中设置会话cookie参数可以通过session_set_cookie_params()函数实现。1)使用该函数设置参数,如过期时间、路径、域名、安全标志等;2)调用session_start()使参数生效;3)根据需求动态调整参数,如用户登录状态;4)注意设置secure和httponly标志以提升安全性。

在PHP中使用会议的主要目的是什么?在PHP中使用会议的主要目的是什么?Apr 22, 2025 pm 05:25 PM

在PHP中使用会话的主要目的是维护用户在不同页面之间的状态。1)会话通过session_start()函数启动,创建唯一会话ID并存储在用户cookie中。2)会话数据保存在服务器上,允许在不同请求间传递数据,如登录状态和购物车内容。

您如何在子域中分享会议?您如何在子域中分享会议?Apr 22, 2025 pm 05:21 PM

如何在子域名间共享会话?通过设置通用域名的会话cookie实现。1.在服务器端设置会话cookie的域为.example.com。2.选择合适的会话存储方式,如内存、数据库或分布式缓存。3.通过cookie传递会话ID,服务器根据ID检索和更新会话数据。

使用HTTP如何影响会话安全性?使用HTTP如何影响会话安全性?Apr 22, 2025 pm 05:13 PM

HTTPS通过加密数据传输、防止中间人攻击和提供身份验证,显着提升了会话的安全性。 1)加密数据传输:HTTPS使用SSL/TLS协议加密数据,确保数据在传输过程中不被窃取或篡改。 2)防止中间人攻击:通过SSL/TLS握手过程,客户端验证服务器证书,确保连接合法性。 3)提供身份验证:HTTPS确保连接的是合法服务器,保护数据完整性和机密性。

继续使用PHP:耐力的原因继续使用PHP:耐力的原因Apr 19, 2025 am 12:23 AM

PHP仍然流行的原因是其易用性、灵活性和强大的生态系统。1)易用性和简单语法使其成为初学者的首选。2)与web开发紧密结合,处理HTTP请求和数据库交互出色。3)庞大的生态系统提供了丰富的工具和库。4)活跃的社区和开源性质使其适应新需求和技术趋势。

PHP和Python:探索他们的相似性和差异PHP和Python:探索他们的相似性和差异Apr 19, 2025 am 12:21 AM

PHP和Python都是高层次的编程语言,广泛应用于Web开发、数据处理和自动化任务。1.PHP常用于构建动态网站和内容管理系统,而Python常用于构建Web框架和数据科学。2.PHP使用echo输出内容,Python使用print。3.两者都支持面向对象编程,但语法和关键字不同。4.PHP支持弱类型转换,Python则更严格。5.PHP性能优化包括使用OPcache和异步编程,Python则使用cProfile和异步编程。

PHP和Python:解释了不同的范例PHP和Python:解释了不同的范例Apr 18, 2025 am 12:26 AM

PHP主要是过程式编程,但也支持面向对象编程(OOP);Python支持多种范式,包括OOP、函数式和过程式编程。PHP适合web开发,Python适用于多种应用,如数据分析和机器学习。

PHP和Python:深入了解他们的历史PHP和Python:深入了解他们的历史Apr 18, 2025 am 12:25 AM

PHP起源于1994年,由RasmusLerdorf开发,最初用于跟踪网站访问者,逐渐演变为服务器端脚本语言,广泛应用于网页开发。Python由GuidovanRossum于1980年代末开发,1991年首次发布,强调代码可读性和简洁性,适用于科学计算、数据分析等领域。

See all articles

热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

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

热工具

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!

mPDF

mPDF

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

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。