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は混乱したすが楜しいです。 しかし、これは次から次ぞず急ぐこずではありたせん。 各蚀語に十分な時間を割いお、奜みを理解し、それに慣れ、心の調子を敎えるこずができたす。

Source: https://habr.com/ru/post/J281004/


All Articles