目次
はじめに
PHP におけるネイティブ型へのオブジェクト指向のアプローチ
API をさらに簡素化する機会
提高了可读性
多态
松散的类型
PHP中的原生类型
对象传递语义
目前的状态
引用
ホームページ バックエンド開発 PHPチュートリアル [nikic master からの翻訳] PHP のネイティブ型のメソッド

[nikic master からの翻訳] PHP のネイティブ型のメソッド

Jun 13, 2016 pm 12:22 PM
array gt quot str string

[nikic master からの翻訳] PHP のネイティブ型のメソッド

はじめに

CET-4 の英語レベルを使用して翻訳するのはこれが初めてです~~囧,不正確な点も多々あるかと思いますが、適当すぎる場合は、お気軽にコメントください(適当な訳が思い浮かばない箇所もあるので、原文と私の拙い訳を載せておきます)。

この記事を翻訳するのに 3 晩と 1 昼かかりました~。まず、それについて話す前に一般的な技術的な側面を理解し、それから翻訳する必要があります~

これは英語を訓練する一方で、比較的新しい技術的アイデアを海外から学ぶためです。

この記事では主に、PHP でのネイティブ型に対するオブジェクト指向操作の実装について説明します。これは、PHP における不規則な関数名、不規則なパラメーターの順序、および可読性の低さの問題を解決するために拡張機能を使用して実装されます。

この拡張機能は、ZEND エンジンでオブジェクト指向メソッドを呼び出すときに、対応する処理関数を変更することで実装されます。IS_STRING の場合はカスタム処理を続行し、オブジェクト指向の呼び出し元のタイプを決定する関数を登録します。 ZEND のデフォルト処理関数に戻り、以下の説明を参照してください。

HOOKPHP コード

PHP はインタープリタ言語であり、コードは次の関数によって中間バイトコードに変換されます。 ZEND エンジンが解析して実行します。 PHP は中間バイトコード OPCODE を呼び出します。各 OPCODE は 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>//不同的函数命名约定strposstr_replace//很不清晰的命名strcspnstrpbrk//颠倒的参数顺序strpos($haystack, $needle)array_search($needle, $haystack)</code>
ログイン後にコピー

これらの問題はしばしば強調されすぎますが (統合開発環境があります)、状況がかなり良好であることを否定するのは困難です。多くの関数には、奇妙な名前をはるかに超えた問題が存在することにも注意してください。多くの場合、エッジ ケースの動作は十分に考慮されていないため、呼び出し側のコードで特別な処理が必要になります。 (文字列関数の場合、エッジ ケースには通常、空の文字列や文字列の範囲外のオフセットが含まれます。)

一般的な提案は、関数の名前とパラメーターの順序を統一するために、PHP6 に膨大な数のエイリアスを実装することです。したがって、stringpos()stringreoplace()stringcomplement_span() などになります。個人的には (これは多くの php-src 開発者の意見のようですが)、これらは私にとってほとんど意味がありません。これらの関数名は現在、PHP プログラマーの記憶に深く根付いているため、重要ではない表面上の変更を実装する価値がないように思えるかもしれません。

一方で、プリミティブ型の OO API の導入は、新しいパラダイムへの切り替えの副作用として API を再設計する機会を提供します)。これにより、古い API の予想される出力を考慮することなく、最初から開始できるようになります。 2 つの例:

  • 我非常希望有$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>nullboolintfloatstringarrayresource</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

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Huawei GT3 ProとGT4の違いは何ですか? Huawei GT3 ProとGT4の違いは何ですか? Dec 29, 2023 pm 02:27 PM

多くのユーザーはスマートウォッチを選ぶときにファーウェイブランドを選択しますが、その中でもファーウェイ GT3pro と GT4 は非常に人気のある選択肢であり、多くのユーザーはファーウェイ GT3pro と GT4 の違いに興味を持っています。 Huawei GT3pro と GT4 の違いは何ですか? 1. 外観 GT4: 46mm と 41mm、材質はガラスミラー + ステンレススチールボディ + 高解像度ファイバーバックシェルです。 GT3pro: 46.6mm および 42.9mm、材質はサファイアガラス + チタンボディ/セラミックボディ + セラミックバックシェルです。 2. 健全な GT4: 最新の Huawei Truseen5.5+ アルゴリズムを使用すると、結果はより正確になります。 GT3pro: ECG 心電図と血管と安全性を追加

Java の String.valueOf() 関数を使用して基本データ型を文字列に変換する Java の String.valueOf() 関数を使用して基本データ型を文字列に変換する Jul 24, 2023 pm 07:55 PM

Java の String.valueOf() 関数を使用して基本データ型を文字列に変換する Java 開発で基本データ型を文字列に変換する必要がある場合、一般的な方法は String クラスの valueOf() 関数を使用することです。この関数は、基本データ型のパラメータを受け入れ、対応する文字列表現を返すことができます。この記事では、基本的なデータ型変換に String.valueOf() 関数を使用する方法を検討し、いくつかのコード例を提供します。

char配列を文字列に変換する方法 char配列を文字列に変換する方法 Jun 09, 2023 am 10:04 AM

char配列をstringに変換する方法:代入によって実現できます {char a[]=" abc d\0efg ";string s=a;} 構文を使用して、char配列にstringに値を直接代入させて実行します変換を完了するためのコード。

修正: Windows 11 で Snipping ツールが機能しない 修正: Windows 11 で Snipping ツールが機能しない Aug 24, 2023 am 09:48 AM

Windows 11 で Snipping Tool が機能しない理由 問題の根本原因を理解すると、適切な解決策を見つけるのに役立ちます。 Snipping Tool が正しく動作しない主な理由は次のとおりです。 フォーカス アシスタントがオンになっている: これにより、Snipping Tool が開かなくなります。破損したアプリケーション: 起動時にスニッピング ツールがクラッシュする場合は、破損している可能性があります。古いグラフィック ドライバー: 互換性のないドライバーは、スニッピング ツールに干渉する可能性があります。他のアプリケーションからの干渉: 実行中の他のアプリケーションが Snipping Tool と競合する可能性があります。証明書の有効期限が切れています: アップグレード プロセス中のエラーにより、この問題が発生する可能性があります。これらの簡単な解決策は、ほとんどのユーザーに適しており、特別な技術知識は必要ありません。 1. Windows および Microsoft Store アプリを更新する

Java の String.replace() 関数を使用して、文字列内の文字 (文字列) を置換します。 Java の String.replace() 関数を使用して、文字列内の文字 (文字列) を置換します。 Jul 25, 2023 pm 05:16 PM

Java の String.replace() 関数を使用して文字列内の文字 (文字列) を置換する Java では、文字列は不変オブジェクトです。つまり、文字列オブジェクトが作成されると、その値は変更できません。ただし、文字列内の特定の文字または文字列を置換する必要がある状況が発生する場合があります。現時点では、Java の String クラスの replace() メソッドを使用して文字列置換を実装できます。 String クラスの replace() メソッドには 2 つのタイプがあります。

2wワード詳細説明文字列、yyds 2wワード詳細説明文字列、yyds Aug 24, 2023 pm 03:56 PM

皆さんこんにちは。今日は Java の基本知識である String についてお話します。 String クラスの重要性は言うまでもなく、バックエンド開発で最もよく使用されるクラスであるため、説明する必要があります。

JavaのString.length()関数を使用して文字列の長さを取得します。 JavaのString.length()関数を使用して文字列の長さを取得します。 Jul 25, 2023 am 09:09 AM

文字列の長さを取得するには、Java の String.length() 関数を使用します。Java プログラミングでは、文字列は非常に一般的なデータ型です。多くの場合、文字列の長さ、つまり文字列内の文字数を取得する必要があります。 Java では、String クラスの length() 関数を使用して文字列の長さを取得できます。簡単なコード例を次に示します。 publicclassStringLengthExample{publ

C# の Array.Sort 関数を使用して配列を並べ替える C# の Array.Sort 関数を使用して配列を並べ替える Nov 18, 2023 am 10:37 AM

タイトル: Array.Sort 関数を使用して C# で配列を並べ替える例 本文: C# では、配列は一般的に使用されるデータ構造であり、多くの場合、配列を並べ替える必要があります。 C# には Array クラスが用意されており、このクラスには配列を簡単に並べ替えるための Sort メソッドがあります。この記事では、C# で Array.Sort 関数を使用して配列を並べ替える方法を示し、具体的なコード例を示します。まず、Array.Sort 関数の基本的な使用法を理解する必要があります。 Array.So

See all articles