PHP中原生類型的方法
引言
第一次,翻譯別人的文章,用四級英語的水平來翻譯~~囧,可能有很多不太恰當的地方,儘管拍磚(有些地方實在想不到恰當的翻譯,我同時貼出了原文和自己很low的翻譯)。
翻譯這篇文章用了我3個晚上一個中午~,先弄清楚技術上大體再說什麼,然後在翻譯的~
這樣做的目的一方面鍛煉下自己的英文,一方面學習點國外的比較新的技術想法。
這篇文章主要講了對PHP中的原生類型實現物件導向的操作,透過擴展的方式實現,用來解決PHP中函數命名不規範、參數順序不規範、可讀性低的問題。
擴充的實作是透過改變ZEND引擎中呼叫物件導向方法時對應的處理函數來實現的,註冊一個函數,判斷物件導向呼叫者的類型,如果是IS_STRING則繼續自訂處理,否則傳回ZEND預設的處理函數,看看下面的解釋
HOOKPHP程式碼
PHP是解釋型語言,程式碼翻譯為中間字節碼由ZEND引擎解析執行。 PHP把中間字節碼稱之為OPCODE,每個OPCODE對應ZEND底層的一個處理函數,ZEND引擎最後執行這個處理函數。實作HOOK功能只需要改變HOOK OPCODE對應的處理函數即可。
PHP中原生類型的物件導向的方法
幾天前,Anthony Ferrara寫下了一些對PHP未來的看法。我同意他的大部分觀點,但不是所有的。這篇文章關注的是一個特別的方面:將像字串、陣列這樣的原生類型偽裝為“偽物件”,可以在這些原生類型上執行方法呼叫。
讓我們從幾個例子開始看看為什麼需要「偽對象」:
1 2 3 4 5 6 7 8 9 10 11 |
|
這裡的$str
只是一個普通的字串,$array
僅僅是一個普通的數組——他們不是對象。我們所做的只是給他們了一點比較像物件的特性,讓他們可以呼叫方法。
注意上面的特性可不是遙不可及,而是現在已經存在了。 PHP擴充scalar_objects允許你在原生PHP類型上面定義物件導向的方法。
原生類型引入物件式的呼叫方法也帶來了許多好處,我會在下面列出來:
一個讓API進一步讓簡潔的機會
聽到最多的最常見的關於PHP的抱怨很可能是標準庫中函數的前後矛盾、命名不清楚,還有前後不一致、順序混亂的參數。一些典型的例子:
1 2 3 4 5 6 7 8 9 10 11 |
|
然而這些問題通常被過度強調了(我們有整合開發環境),很難否定狀況還是相當好的。還應該指出的是,許多函數表現出的問題遠遠超過了詭異的名字這個問題。通常邊緣情況的行為是沒有被完全考慮的,因而有了在呼叫程式碼中對它們進行特殊處理的需要。 (對字串函數來說,邊緣情況通常包括空字串或超出字串範圍的偏移量。)
一個一般的建議是在PHP6中實現巨大數量的別名,用來統一函數名稱和參數順序。因此我們將會有stringpos()
,stringreoplace()
,stringcomplement_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)
這樣的方法,這些是函數功能是普遍接受的名字(與explode
和implode
不同)。另一方面如果有一個stringsplit($delimiter)
方法有這樣的行為我會感到非常反感,因為已經存在的str_split
函數所做的是完全不同的(分組)。
- 我理所當然的喜歡新的對錯誤報告使用異常的API,由於這是一個面向對象的API,那是自動給定的,異常當然也是和重命名的API一起使用,然而這違背了現在所有的程式函數對錯誤處理使用警告的慣例,這不是一成不變的,但這確實是我想避免的一個爭議點:)
這是我實現原生類型物件導向API的主要動機:從零開始,允許我們實現一套合理的API設計。當然了,這個改動的所有好處不知這些。物件導向的語法提供了許多更深層的好處,將在下面討論。
提高了可读性
程序式的调用一般没有链式调用好。考虑下面的例子:
1 2 3 4 5 |
|
咋一看,哪个是arraay_map
和array_filter
各自的使用?(原文:what are array_map and array_filter applied to? )他们调用的顺序是什么?变量$input
隐藏在两个闭包之间,函数的书写顺序也和他们实际调用的顺序相反。现在同样的例子使用面向对象的语法:
1 2 3 4 5 |
|
我敢说使用这种方式,操作的顺序(先filter
在map
)和初始输入数组$input
更加明显。
这个例子明显有人为拼凑的感觉,因为array_map
和array_filter
是函数参数顺序颠倒的另外一个例子(这就是为什么输入数组在中间)。再看另外一个输入参数在同一个位置的例子(来自实际的代码):
1 |
|
在这个例子中,最后面是一连串的额外的参数'_'), '\\', '_'), 15,
,很难把这些参数和应用的函数对应起来。把这个和使用面向对象方法的版本做个比较:
1 |
|
这次函数运算和他们的参数紧密的联系在了一起,而且方法的调用和他们的执行顺序相匹配。
另一个来自这种语法的可读性的好处是needle/haystack
不明确问题。别名通过引入统一的参数顺序规范让我们解决了这个问题,使用面向对象的API这个问题基本不存在了。
1 2 3 4 5 |
|
这里哪个部分应用了哪个规则的困惑不复存在了。
多态
目前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.
为了阐明这个问题,考虑下面的例子:
1 2 |
|
这里$num
被作为一个字符串数字,被str_split
切分后使用array_sum
求和。现在试试同样效果的面向对象方法调用:
1 2 |
|
这里字符串的cheunk()
方法被数字来调用。会发生什么??Anthony建议这样解决:
这意味着所有的标量运算将必然需要对应的标量类型。这将导致需要一个标量有所有的数学方法的对象模型,当然包括所有的字符串方法。真是一个噩梦。。。。。
就像引言中所说的那样,这绝不是一个可接受的解决方法。然而我想我们可以绝对侥幸的逃脱仅仅在那种情况的时候抛出一个错误(异常!)。为了解释为什么这种方法是可行的,让我们看看PHP可以拥有哪些类型。
PHP中的原生类型
除了对象之外,PHP有下面的变量类型:
1 2 3 4 5 6 7 |
|
现在,我们考虑下上面这些里面的哪些会需要面向对象的方法:我们首先去掉resource
,然后在剩下的里面看。null
和bool
明显不需要面向对象的方法,除非你想进行像$bool->invert()
这样无聊的转换。
绝大多数的数学函数使用面向对象的方法也不是很合适。考虑下面几个例子:
1 2 3 |
|
我想你会同意数学函数可读性比函数符号的形式更好。当然存在少许的面向对象方法你可以适当的应用数字类型,比如说$num->format(10)
读起来相当的不错。然而,关于这里,对于一个面向对象的数字API不是真正的需要,只有少量的函数你可能需要。(再者来说目前的数学API在命名方面没有太多的问题,而且数学操作相关的命名相当的标准。)
现在剩给我们的只有字符串和数组了,我们已经看到这两种类型有许多很棒的API。但关于松散类型的问题我们所有的必须要做的有哪些?下面是重要的几点:
我们经常性的把字符串视为数字的时候(例如来自HTTP或者DB),这样反过来是不对的:直接将数字作为字符串非常少见。举个栗子,下面的代码将让我们感到困惑:
1 |
|
这样将数字视为字符串是一个很怪异的操作,这种情况也ok啊,只需要强制转换一次就好了。使用原来的求和数字的例子:
1 2 |
|
现在我们弄明白了,是的,我们确实想将数字视为字符串。对我来说,这样来处理想这样使用这种技术的地方是可以接受的。
对于数组情况就更简单了:他不会出现讲一个数组操作视为一个其他不是数组类型的操作。
另一方面可以通过标量类型提示改善这个问题(我完全认为在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中的对象和其他类型相比有不同的传递语义(某种程度上和引用类似)。如果我们允许字符串和数组进行面向对象的方法调用,他们看起来会和对象很像,那样的话有些人可能期望他们有对象作为参数的传递语义。这个问题在在字符串和数组中都存在:
1 2 3 4 5 6 7 8 9 10 |
|
我们当然将会改变传递语义。首先,在我看来通过值传递来传递像数组这种大的数据结构是一个相当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扩展实现了这个功能。它允许你注册一个处理各自的原生类型的方法调用的类。看一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
不久前,我开始了一个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教程有兴趣的朋友有所帮助。

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

JWT是一種基於JSON的開放標準,用於在各方之間安全地傳輸信息,主要用於身份驗證和信息交換。 1.JWT由Header、Payload和Signature三部分組成。 2.JWT的工作原理包括生成JWT、驗證JWT和解析Payload三個步驟。 3.在PHP中使用JWT進行身份驗證時,可以生成和驗證JWT,並在高級用法中包含用戶角色和權限信息。 4.常見錯誤包括簽名驗證失敗、令牌過期和Payload過大,調試技巧包括使用調試工具和日誌記錄。 5.性能優化和最佳實踐包括使用合適的簽名算法、合理設置有效期、

靜態綁定(static::)在PHP中實現晚期靜態綁定(LSB),允許在靜態上下文中引用調用類而非定義類。 1)解析過程在運行時進行,2)在繼承關係中向上查找調用類,3)可能帶來性能開銷。

PHP的魔法方法有哪些? PHP的魔法方法包括:1.\_\_construct,用於初始化對象;2.\_\_destruct,用於清理資源;3.\_\_call,處理不存在的方法調用;4.\_\_get,實現動態屬性訪問;5.\_\_set,實現動態屬性設置。這些方法在特定情況下自動調用,提升代碼的靈活性和效率。

PHP和Python各有優勢,選擇依據項目需求。 1.PHP適合web開發,尤其快速開發和維護網站。 2.Python適用於數據科學、機器學習和人工智能,語法簡潔,適合初學者。

PHP在電子商務、內容管理系統和API開發中廣泛應用。 1)電子商務:用於購物車功能和支付處理。 2)內容管理系統:用於動態內容生成和用戶管理。 3)API開發:用於RESTfulAPI開發和API安全性。通過性能優化和最佳實踐,PHP應用的效率和可維護性得以提升。

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

PHP仍然具有活力,其在現代編程領域中依然佔據重要地位。 1)PHP的簡單易學和強大社區支持使其在Web開發中廣泛應用;2)其靈活性和穩定性使其在處理Web表單、數據庫操作和文件處理等方面表現出色;3)PHP不斷進化和優化,適用於初學者和經驗豐富的開發者。

在PHP8 中,match表達式是一種新的控制結構,用於根據表達式的值返回不同的結果。 1)它類似於switch語句,但返回值而非執行語句塊。 2)match表達式使用嚴格比較(===),提升了安全性。 3)它避免了switch語句中可能的break遺漏問題,增強了代碼的簡潔性和可讀性。
