PHP vs. Ruby:一緒に生きよう





1つの言語を上手に話し、自分で新しい言語を試してみた開発者が、性急な結論と比較を行うことがよくあります。 通常、このような出版物はかなり役に立たないが、キャッチーな見出しは良いトラフィックを与えます。



私は、両方の言語が好きで、それらの言語を使った経験が豊富な開発者の観点から、より正直な比較を行うことがはるかに興味深いと判断しました。 たとえば、PHPとRubyを使用します。 そして、ここでのタスクは、どれが「より良い」かを見つけることではありません。 Rubyとそのエコシステムで気に入っているプロパティを強調したいだけです。



概念的な違い



言語の違いはよりイデオロギー的ですが、最良の言語を選択するために、異なる言語はしばしば互いに比較されます。 言語のまったく同じ特性は、一部の開発者にとっては恩恵であり、他の開発者にとっては悪夢です。 要点を説明する前に、言語間の基本的な違いについて説明する必要があります。



メソッド、変数、プロパティ?



PHPは、Rubyとは異なり、プロパティ、メソッド、変数にアクセスするために異なる構文を使用します。



Php

$this->turtle #   $this->bike() #  $apple #  Ruby @turtle #   turtle # " "   attr_reader: :turtle bike #  apple # 
      
      





Pedantsはattr_reader :turtle



attr_reader :turtle



のゲッターとして使用されるメソッドを動的に定義することに気付くでしょう。 @turtle



turtle



bike



は同じものです。 同時に、PHP開発者はどこから来たのか理解できず、メソッド名または変数を明示的に定義せずにturtle



の使用を検討します。



これにより混乱が生じる場合があります。 たとえば、チームメイトはattr_reader



要素を完全なメソッドに、またはその逆に置き換えて混乱を引き起こします。 非常に柔軟なAPIを使用する場合、これは完全に受け入れられる動きであり、非常に大胆なスキームを実装できることを言わなければなりません。



フィールドを削除しましたが、JSONコントラクトはまだ存在していると考えていますが、まだ多くのコードが残っていますか?



 class Trip def canceled_at nil end end
      
      





これは正常に機能します。何かがtrip.canceled_at



呼び出すと、フィールドではなくnil



が取得されます。 後でこれを正しく削除できます。



タイプヒントとアヒルタイピング



PHPの世界では、型の指示は奇妙で素晴らしいものです。 Goなどの言語では、引数の型と戻り値の型の両方を指定する必要があります。 引数のオプションの型指定は、5番目のバージョンからPHPに登場しました。 また、最近呼び出されたオブジェクトだけでなく、配列、特定のクラス名、インターフェイス、または抽象オブジェクトを要求することもできます。



PHP 7.0では、戻り値の型指示、およびint



string



float



などのヒントサポートがありました。 さらに、 スカラー型の指示が導入されました。 この機能は多くの論争と議論を引き起こしましたが、結果として実装されました。 その使用は、プログラマー自身の好みに完全に依存します。これは、PHPユーザーの多様性に照らして朗報です。



Rubyにはこれはありません。



ダックタイピングは、PHPコミュニティの一部によってサポートされている優れたアプローチです。 「引数はFooInterface



を実装するクラスのインスタンスでなければならない」と述べる代わりに、 FooInterface



bar(int $a, array $b)



メソッドbar(int $a, array $b)



を持つことになり、そうでなければ「引数は何でもFooInterface



ん。 bar



メソッド。 そして、彼が反応しなければ、他の何かを思いつきます。」



ルビー

 def read_data(source) return source.read if source.respond_to?(:read) return File.read(source.to_str) if source.respond_to?(:to_str) raise ArgumentError end filename = "foo.txt" read_data(filename) #=>   foo.txt    File.read() input = File.open("foo.txt") read_data(input) #=>   foo.txt    #   
      
      





これは本当に柔軟なアプローチですが、このコードが気に入らない人もいます。 特にPHPでは、weakモードのint(0)



int(1)



が任意の値を取る正しいブール値と見なされるため、すべてが正常に機能することを期待するのは非常に危険です。 PHPでは、2つの異なるメソッド/関数を簡単に定義できます。



 function read_from_filename(string $filename) { $file = new SplFileObject($filename, "r"); return read_from_object($file); } function read_from_object(SplFileObject $file) { return $file->fread($file->getSize()); } $filename = "foo.txt"; read_from_filename($filename); $file = new SplFileObject($filename, "r"); read_from_object($file);
      
      





言い換えれば、PHPでダックタイピングを簡単に使用できます。



 function read_data($source) { if (method_exists($source, 'read')) { return $source->read(); } elseif (is_string($source)) { $file = new SplFileObject($source, "r")); return $file->fread($file->getSize()); } throw new InvalidArgumentException; } $filename = "foo.txt"; read_data($filename); #=>   foo.txt    # SplFileObject->read(); $input = new SplFileObject("foo.txt", "r"); read_data($input); #=>   foo.txt    #   
      
      





ダックタイピングを使用する可能性があるため、Rubyが「より良い」という非常に一般的な誤解はPHPです。 PHPでは、両方のアプローチを使用できます。どちらかを選択してください。 そして、このPHPは、本当にしたい場合でも、タイプヒントを使用する方法がないRubyと比較して有利です。 多くのPHPプログラマーは、実際に型参照を嫌い、それらをまったく避けたいと考えています。 しかし残念なことに、PHP 7.0にはさらに多くのタイプヒントがあります。



ところで、Pythonの前には型ディレクティブもありませんでしたが、最近ではそれでも実装ています



楽しい機能



上記に対処した後、あなたは楽しいことに集中することができます。 定期的にではなくても、それらのいくつかを非常に頻繁に適用できます。



ネストされたクラス



PHP開発者にとって、これは非常にエキゾチックなものです。 クラスは名前空間に存在しますが、クラスとスペース自体は同じ名前を持つことができます。 したがって、特定の1つのクラスのみに関連するクラスがある場合は、それを名前空間に単純に導入します。 ExplodingBoxException



をスローできるBoxクラスをExplodingBoxException



ます。



 namespace Acme\Foo; class Box { public function somethingBad() { throw new Box\ExplodingBoxException; } }
      
      





この例外はどこかに存在する必要があります。 クラスの最上位に置くことができますが、1つのファイルに2つのクラスがあります...一般に、多くの人を楽しませます。 PSR-1。



「これは、各クラスが個別のファイルにあり、名前空間では少なくとも1つのレベル、つまりトップレベルプロバイダーの名前が必要であることを意味します。」


以下がその個別のファイルです。

 namespace Acme\Foo\Box; class ExplodingBoxException {}
      
      





この例外をロードするには、オートローダーを使用してファイルシステムに再度アクセスする必要があります。 しかし、リソースはこれに費やされています! PHP 5.6では、オペコードキャッシュをオンにすると、繰り返されるリクエストの冗長性が低下しますが、それでも余分な作業が発生します。



Rubyでは、あるクラスを別のクラスにネストできます。

 module Acme module Foo class Box class ExplodingBoxError < StandardError; end def something_bad! raise ExplodingBoxError end end end end
      
      





これは、クラスを定義するときとクラス外で行うことができます。



 begin box = Acme::Foo::Box.new box.something_bad! rescue Acme::Foo::Box::ExplodingBoxError # ... end
      
      





おそらく奇妙に見えますが、非常に便利です。 クラスは1つのクラスのみに関連していますか? それらをグループ化します!



別の例は、データベースの移行に関連しています。 CodeIgniterからLaravelまで、多くの一般的なPHPフレームワークで使用されています。 移行中にモデルまたは別のクラスを参照してから変更すると、古い移行は非常に複雑になります。



Rubyでは、この問題はネストされたクラスを使用して美しく解決されます。



 class PopulateEmployerWithUserAccountName < ActiveRecord::Migration class User < ActiveRecord::Base belongs_to :account end class Account < ActiveRecord::Base has_many :users end def up Account.find_each do |account| account.users.update_all(employer: account.name) end end def down #  ,  ID ,     # « » User.where.not(account_id: nil).update_all(employer: nil) end end
      
      





グローバルに宣言されたクラスの代わりに、 User



およびAccount



ORMモデルのネストされたバージョンが使用されます。 つまり、必要に応じて、それらは私たちのスナップショットとして機能します。 これは、ゲームのルールがいつでも変更できる条件でコードを呼び出すよりもはるかに便利です。 一部の人にとっては、移行の問題に出くわすまで、これはすべてワイルドに聞こえます。



デバッガー



XDebugは素晴らしいものです。 ブレークポイントの使用は、PHPアプリケーションのデバッグに小さな革命をもたらし、初心者開発者の間で広く使用されているスキームよりも高度なvar_dump()



+更新スキームの実装を可能にしました。



つまり、XDebugをIDEで動作させ、適切なアドオンを見つけ(必要に応じて)、php.iniを設定してzend_extension=xdebug.so



をCLIおよびアプリケーションのWebバージョンで使用し、マイルストーンを送信するのにzend_extension=xdebug.so



可能zend_extension=xdebug.so



があります、Vagrantなどを使用している場合でも



Rubyのアプローチは少し異なります。 ブラウザーでJavaScriptをデバッグする場合と同様に、ワードdebugger



をコードに挿入するだけでブレークポイントを取得できます。 この行が実行されるとき、それが$ rails server



、単体テスト、統合テストなどであることは問題ではありません。 -コードを使用できるREPLインスタンスにアクセスできます。



さらにいくつかのデバッガーがあり、そのうち最も人気のあるものはpry





pry





そしてbyebug



。 どちらもgemであり、それらをインストールするには、Bundlerを介してGemfile



コードを追加する必要があります。



 group :development, :test do gem "byebug" end
      
      





これは、dev Composer依存関係の類似物です。 Railsを使用する場合は、インストール後に、 debugger



呼び出すだけです。 そうでない場合は、まずrequire "byebug"



実行するrequire "byebug"



があります。



Railsチュートリアルでは、アプリケーションに適切なキーワードを埋め込んだ後、すべてがどのように機能するかを説明しています。



 [1, 10] in /PathTo/project/app/controllers/articles_controller.rb 3: 4: # GET /articles 5: # GET /articles.json 6: def index 7: byebug => 8: @articles = Article.find_recent 9: 10: respond_to do |format| 11: format.html # index.html.erb 12: format.json { render json: @articles } (byebug)
      
      





矢印は、REPLインスタンスを開始する行を指します。 コードから直接コードの実行を開始できます。 その場所では、 @articles



まだ定義され@articles



いませんが、 Article.find_recent



を呼び出して何が起こるかを見ることができます。 エラーが発生した場合、「 next



と入力して同じコンテキストの次の行に移動できます。 または、 step



と入力して、次のステートメントを実行します。



これは、特にコードが必要なものを出力しない理由を理解しようとしている場合に便利です。 このコンテキストで各値の動作を確認し、実行結果に関係なく作業コードをファイルにコピーできます。 テストに非常に便利です。



しない限り



多くの人はそうでないunless



好きではありません。 他の多くの言語の他の多くのものと同様に、それらはしばしば虐​​待されます。 2008年の記事に記載され Unless



いるUnless



、長年にわたって人々をUnless



きた場合を除きます



except構造はif



「反意語」 if



。 条件がtrue



場合、コードはブロックされ、 false



場合、実行は継続されfalse







 unless foo? # bar that thing end # Or def something return false unless foo? # bar that thing end
      
      





これにより、特に多数の条件下で作業が少し簡単になります。 使用できます|| および括弧。 この行の代わりに:



 if ! (foo || bar) && baz
      
      





書くことができます:



 unless (foo || bar) && baz
      
      





おそらくこれは多すぎるので、 else



を使用しunless



else



使用できませelse



が、それ自体は便利なツールです。 PHPでのその実装は2007年に依頼されましたが、しばらくの間、要求は無視されました。 最後に、PHPの作成者であるRasmus Lerdorfは、 unless()



関数は後方互換性に違反するため、「英語が母国語ではない人には明らかではない」と述べました。



「これは「no if」を意味する奇妙な単語ですが、「less」が「more」の反対であることに論理的に類似している必要がありますが、接頭辞「un」はその意味を反対(less-限り)。」


物議を醸す声明。 でunless



、人々がこの単語を読むとき、接頭辞un



ためだけに、その意味を「以下の反対」とは認識しません。 そうでない場合、 uniqid()



関数を読み取り、 uniqid()



の反対とiqid()



ます。



述語メソッド



Rubyには、PHPで異なる方法で解決された興味深い規則が数多くあります。 それらの1つは、述語メソッド、つまりブール型の応答タイプを持つメソッドです。 型参照がRubyで返されないことを考えると、このようなメソッドは役立つ場合があります。



例えば、 object.nil?



など、述語メソッドの多くはすでにRubyに組み込まれていobject.nil?



。 実際、これはPHPの$object === nil



類似物です。 アクションを実行しないで何かを要求する必要がある場合、 include?



を使用する方が良いinclude?



include



代わりに。



独自の述語メソッドを設定することもできます:



 class Rider def driver? !potential_driver.nil? && vehicle.present? end end
      
      





述語メソッドを作成するとき、多くのPHP開発者は接頭辞is



および/またはhas



を名前に追加し、たとえばisDriver()



またはhasVehicle()



ます。 しかし、時には他のプレフィックスを見つけることができます。 can_drive?



メソッドと言いcan_drive?



RubyのPHPではcanDrive()



になりますが、これが述語メソッドであることは完全には明らかではありません。 伝統によれば、 isAbleToDrive()



ような名前に変更する方が良いでしょう。



さらに簡潔な配列構文



PHPでは、リテラル配列を簡単に定義できます。PHP5.4以降では、さらに簡潔な構文があります。



 // < 5.4 $a = array('one' => 1, 'two' => 2, 'three' => 'three'); // >= 5.4 $a = ['one' => 1, 'two' => 2, 'three' => 'three'];
      
      





誰かがこの場合、 簡潔にそれを少し上書きしたと言うかもしれません。 Ruby 1.9では、=>をセミコロンに置き換えることができるオプションが登場しました。 PHPでは、さらに先へ進むことができます。



 $a = ['one': 1, 'two': 2, 'three': 'three'];
      
      





ネストされた配列を1日に数百回入力する必要がある場合など、場合によってはこれは非常に便利です。 構文を簡素化する提案は2011年に投稿されましたが、まだ受け入れられていません。 おそらく彼らはしません。 PHPでは、新しいものが多くの利点を約束している場合でも、最小限の構文を維持しようとし、古いものを実装する新しい方法を導入することはほとんどありません。 シンタックスジンジャーブレッドCookieはPHPチームにとっての優先事項ではありませんが、Rubyではこれがほぼ主な方向です。



オブジェクトリテラル



この機能はRubyで利用可能で、多くの開発者がPHPで見たいと思っています。 ここで値を使用してStdClass



クラスを定義する必要がある場合、2つの方法があります。



 $esQuery = new stdClass; $esQuery->query = new stdClass; $esQuery->query->term = new stdClass; $esQuery->query->term->name = 'beer'; $esQuery->size = 1; //  $esQuery = (object) array( "query" => (object) array( "term" => (object) array( "name" => "beer" ) ), "size" => 1 );
      
      





PHPでは常にこのようになっていますが、もっと簡単にできます。 ほぼ完全に構文をRubyから借用したとしましょう:



Php

 $esQuery = { "query" : { "term" : { "name" : "beer" } }, "size" : 1 };
      
      





ルビー

 esQuery = { "query" : { "term" : { "name" : "beer" } }, "size" : 1 }
      
      





この革新をPHPで見たいと思っていますが、これまでのところ開発者の関心はありません。



救助方法



PHPにはtry/catch



があり、Rubyではbegin/rescue



がありbegin/rescue



。 特にPHP 5.6の登場に照らして、Rubyのシュアの類似物として、ほとんど同じように機能します。 どちらの言語でも、 try



およびbegin



コマンドが機能begin



任意の場所から除外した後に回復する可能性があります。 しかし、Rubyではさらに多くのことができます。beginメソッドをスキップして、関数/メソッドの本体から直接回復できます。



ルビー

 def create(params) do_something_complicated(params) true rescue SomeException false end
      
      





何かがうまくいかない場合は、なんらかの方法でエラーを処理し、怒ったり、誰かに電話して解決したりすることはできません。 これは万能薬ではありませんが、 begin



すべてをラップすることなく、さまざまな可能性を持っている方が良いです。



残念ながら、このアプローチはPHPでは機能しませんが、実装された場合、コードは次のようになります。



 function create($params) { do_something_complicated($params); return true; } catch (SomeException $e) { return false; }
      
      





一見したところ、そのことはそれほど重要ではありません。 しかし、Rubyの非常に多くの小さなアメニティが、言語が本当にあなたを助けようとし、有用であると感じているのです。



例外後の再試行



非常に便利なツールは、 retry



コマンドです。



 begin SomeModel.find_or_create_by(user: user) rescue ActiveRecord::RecordNotUnique retry end
      
      





この例では、 find_or_create_by



非アトミック性により、言語間の競合が再び悪化していfind_or_create_by



(ORMは最初にSELECT



を実行してからINSERT



実行します)。 また、運が悪ければ、別のプロセスがSELECT



後、 INSERT



前にレコードを作成する場合があります。



これはすでに発生する可能性があるため、 SELECT



使用して、 begin...rescue



を繰り返し実行しようとする可能性SELECT



提供することをおSELECT



ます。 おそらく、場合によっては、ここに何らかのロジックを1つまたは2つ出力するためにここに配置したいこともありますが、このオプションは考慮しません。 コードを再実行する便利さをよりよく理解しましょう。 Rubyでは、これは次のように実行できます。



 def upload_image begin obj = s3.bucket('bucket-name').object('key') obj.upload_file('/path/to/source/file') rescue AWS::S3::UploadException retry end end
      
      





また、PHPでは、最初に初期ブロックのコンテンツ用の新しい関数/メソッドを作成する必要があります。



 function upload_image($path) { $attempt = function() use ($path) { $obj = $this->s3->bucket('bucket-name')->object('key'); $obj->upload_file($path); }; try { $attempt(); } catch (AWS\S3\UploadException $e) $attempt(); } }
      
      





どちらの場合も、何らかの種類のテンプレートブランクを使用してサイクルを停止することができます。 しかし、再実行に関しては、Rubyコードのほうがずっときれいです。 うわさによると、この機能に関して積極的な作業が進行中です。 おそらく、PHP 7.1で表示されるでしょう。



最後にいくつかの考え



少し前まで、私はPHPと同じ方法でRubyで書いていることに気付きました。 しかし、強力で経験豊富なRuby開発者と協力して、いくつかの独特でわずかに異なるアプローチを学びました。 そのため、この記事では、PHPに戻った場合にこの言語で見逃していたものを反映しましたが、戻ってこないようにするほどではありません。 多くのPHP嫌悪者は、PHPで導入された革新を無視しています。 また、Rubyに典型的な便利で便利なものはありませんが、PHP 7には他にも非常に興味深い機能がたくさんあります。



近年、PHPは一貫性の点で特に改善されました。特に、 統一された変数構文コンテキスト依存レクサー、および抽象構文ツリーのためです。 これにより、標準ライブラリの一貫性の程度に関係なく、PHPの一貫性が大幅に向上します。



近い将来、標準ライブラリが改善される可能性は低いため、新しい便利な機能と構文手法の出現のみを歓迎する必要があります。 他の言語でも好きなだけ実験し、さまざまな理論や斬新さを試し、プログラマーリソースのニュースフィードに食料を提供できます。 そして、PHPはまさにこの言語が必要とするものを借用します。 これは効果的なアプローチです。 PHPの海賊略奪者と見なすことできます



時間と希望があれば、さまざまな言語を試してください。 Rubyは良いスタートとなり、Goはとても楽しく、Elixirは混乱しますが楽しいです。 しかし、これは次から次へと急ぐことではありません。 各言語に十分な時間を割いて、好みを理解し、それに慣れ、心の調子を整えることができます。



All Articles