PHPとRubyの文法面での比較

PHPとRubyの文法面および主要な機能にのみ注目して(ラフな)比較を行った。そのため、各種関数などにはほとんど触れていない。

式と文

やや便宜的な説明になるがPHPの式および文の特徴を以下に示す。

Rubyの式および文の特徴を以下に示す。

ブロック

「ブロック」といったときPHPとRubyで意味するところはかなり違う。

まずPHPでいうブロックは以下のように記述されるものである。

{ <文>... }

これは複数の文を一つにまとめるための記法であり、それ以上の特別な意味はなく、スコープを作ることもない。通常はifforなどで複数の文を記述するために使われる。

これに対しRubyでいうブロックは以下のように記述されるものである。

proc { ... }
proc do ... end

method(...) { ... }
method(...) do ... end

{}の部分がブロックの主体であり、どちらのケースでもその内容をオブジェクト化する。オブジェクトはcallメソッド(やyieldメソッド)によって実行できる。

PHPのブロックとは違ってブロックが独自のスコープを作る。また、{}の中に書かれた文だけでなく、そのコンテキストの一部を含めてオブジェクト化するため、いわゆるクロージャとして機能する。

def create_block(s, n)
  local_var = s
  proc {local_var += n}
end
b1 = create_block(0, 3)
b2 = create_block(0, -5)
        #   b1  b2
b1.call #=> 3
b1.call #=> 6
b2.call #=>     -5
b1.call #=> 9
b2.call #=>     -10

定数

PHPの定数は以下のような特徴を持つ。

Rubyの定数は以下のような特徴を持つ。

変数

PHPの変数名は以下のルールに基く。

PHPにはローカル変数、グローバル変数、オートグローバル変数の三類種の変数がある。

ローカル変数
変数が最初に使用されたスコープでのみ有効な変数。
グローバル変数
スクリプト中のどこからでもアクセスできる変数。ただし、関数内からアクセスする場合にはglobalを使った宣言が必要となる。
オートグローバル変数
PHP内部で定義されるグローバル変数でユーザ定義ができない。ただし関数内からの参照でもglobal宣言が不要。

Rubyの変数はローカル変数、インスタンス変数、クラス変数、グローバル変数といった種類があり、それぞれ変数名の最初の数文字で種類を表す。

ローカル変数
_か英小文字で始まり、_または英数字で構成される。ローカルスコープからアクセスできる。ローカルスコープには以下の種類があり、最も内側のものが優先される。
インスタンス変数
@で始まり、_または英数字で構成される。そのオブジェクトのクラスのメソッド(インスタンスメソッド)からアクセスできる。
クラス変数
@@で始まり_または英数字で構成される。そのクラスとサブクラスと各クラスのインスタンスのメソッド(クラスメソッドとインスタンスメソッド)からアクセスでき、外部からはアクセスできない。クラスのインスタンス変数との違いに注意。
グローバル変数
$で始まり、_または英数字で構成される。スクリプト中のどこからでもアクセスできる。ただし、$で始まる変数の一部のはグローバル変数ではないことに注意が必要(正規表現使用時の$1$9など)。

各変数ともそれぞれ、最初に代入が行われた場所での、それぞれが対応するスコープに所属することになる。

なおこれら以外に疑似変数と呼ばれるものがる。

データ

データ型

PHPのデータ型

PHPのデータには以下の分類と種類がある。

var_dump()関数を使うとデータ型やその内容を分かりやすく表示することができる。

各データ型は演算や比較に際して相互に自動変換される(type jugglingと呼ぶ)。データ同士の比較については以下の通り。

意味上の比較

==!=を用いる。自動型変換を伴うため、たとえば0 == ''は真になる。関連して、0''falsenullはすべて偽として扱われるため以下が成り立つ。

0     == FALSE // true
''    == FALSE // true
false == FALSE // true
null  == FALSE // true
厳密な比較

===!==を用いると型変換を伴わず、かつ、データ型の違いを考慮にいれた比較を行うことができる。このため0 === ''は偽になる。

0     === FALSE // false
''    === FALSE // false
false === FALSE // true
null  === FALSE // false

Rubyのデータ型

PHPのような意味でのデータ型はなく、すべてクラスで表現されている。

自動変換は限られたケースを除いて行われない。たとえば以下の場面で変換が行われる。

実際にはある種のメソッドが、引数で与えられたオブジェクトに対して内部で特定のメソッドを呼び、得られた戻り値を対象に処理を行おうとしているだけであって、言語そのものには自動変換はない。

PHPのvar_dump()関数にあたるメソッドしてはpがあり、オブジェクトのクラスやその内容を表示できる(ppライブラリを使うとより見やすい形になる)。

Rubyにおけるオブジェク同士の比較については、通常以下の三種類がある(クラスによっては比較に類するその他のメソッドを提供している場合もある)。

意味上の比較

==!=を用いると意味的に等しいかどうかという点での比較ができる(実際には各クラスで定義されるメソッドである)。

0 == 0.0 #=> true
1 == 1.000000000000000  #=> true
1 == 1.000000000000001  #=> false
1 == 1.0000000000000001 #=> true
厳密な比較

eql?メソッドを用いると引数で与えらたオブジェクトとの厳密な比較を行う(ように各クラスで定義されている)。通常はクラスの違いなども考慮された結果となる。

1  ==  1.0000000000000001  #=> true
1.eql?(1.0000000000000001) #=> false
一致検査

equal?メソッドは引数で指定されたオブジェクトが自分自身であるときだけ真を返す(通常再定義してはならない)。

s1 = "abc"
s2 = "abc"
s1 == s2      #=> true
s1.eql?(s2)   #=> true
s1.equal?(s2) #=> false

データへのアクセスと代入

PHPの場合

基本的に代入はコピーになる。ただしリファレンスカウント機能により、ぎりぎりまで実際のコピーを遅らせることができるようになっている(ユーザは気にしなくてよい)。

このため変数とデータは一対一に対応することになる。複数の変数から同じデータを扱いたとき(関数から元データをいじりたいときなど)にはリファレンスを使った代入を用いる。

$a = "foo";
$b =  $a;
$c = &$a;
$a .= "bar"; /* $aと$cはどちらも"foobar"になり
                $bは"foo"のままになる */
$c = "baz";  /* $aと$cはどちらも"baz"になり
                $bは"foo"のままになる */

PHPのリファレンスは変数に対して別名を付けるイメージに近い。この点でC言語のポインタやRubyでのリファレンスとは意味合いが異るといえる。

PHP4でのオブジェクト型の扱いは他のデータと同様だが、PHP5のオブジェクト型については、オブジェクトそのものではなくオブジェクトハンドルを扱う形になっており、実質的にリファレンスの扱いに近くなっている。

class foo { var $name = null; }
$a = new foo;
$b = $a;
$a->name = "foo;
var_dump($a->name, $b->name);

PHP4ではそれぞれ"foo"NULLを示し、PHP5ではどちらも"foo"を示す。

Rubyの場合

基本的に変数が持つのはオブジェクトへのリファレンスになるため、代入が扱うのもリファレンスになる。ここでいうリファレンスはPHPのようなものではなくC言語のものに近い。

a = "foo"
c = a      # (a)
a << "bar" # (b)
c = "baz"  # (c)

ac(a)において同じ文字列オブジェクトを参照するようになったため、(b)での文字列オブジェクトそのものに対する変更(破壊的な変更と呼ぶ)により、aで参照してもcで参照しても同じく"foobar"が得られる。一方、(c)ではcによって別の文字列オブジェクトを参照するようしたため、この時点でacによってそれぞれ別のオブジェクト("foobar""baz")にアクセスできるようになる。

オブジェクトは一つ、リファレンスは複数というのが一般的な状態であり、オブジェクトのコピーを作りたいときには明示的に行う必要がある。

主なデータの表現

文字列

PHPの文字列表現は以下の通り。

''
書かれた文字列そのものを表す。'abc $foo \n'"abc $foo \\n"になる。
""
エスケープ文字や変数の展開を行う。"abc $foo \n""abc <fooの値> \n"に展開される。
``
文字列をシェルで実行し、その出力で置き換える。
ヒアドキュメント

<<<に続けて識別子を記述することで始める。続く行のうち、行頭に識別子だけまたは識別子と;だけがある行までの部分を文字列の記述として扱う。

$a = <<<E
foo
bar
E;

$a"foo\nbar\n"が代入される。

if (<<<E
foo
E
== <<<E
foo
E
) { echo "OK\n"; }

if ("foo\n" == "foo\n") { echo "OK\n";}と同じ。

文字列の連結
各種文字列表現を.で連結することができる。"foo" . "bar""foobar"と同じ意味になる。

対応するRubyの文字列表現は以下の通り。

''
書かれた文字列そのものを表す(PHPと同じ)。%q!!のような記法によっても同等の表現ができる(!にあたる部分には任意の文字を使用できる)。
""
エスケープ文字の展開を行う。ただしPHPとは違って変数の展開は行わない。%Q!!または%!!のような記法も使用できる。
``
シェルによるコマンド実行の出力結果で置き換える(PHPと同じ)。%x!!のような記法も使用できる。
ヒアドキュメント

<<に続けて識別子を記述することで始める。続く行のうち、行頭に識別子だけがある行までの部分を文字列の記述として扱う。一行に複数のヒアドキュメントを書くこともできる。

a = <<E
foo
bar
E

a"foo\nbar\n"にアクセスできるようになる。

if <<E == <<F
foo
E
foo
F
  puts "OK"
end

if "foo\n" == "foo\n" then puts "OK" endと同じ。

ヒアドキュメント記法について詳細は次のような文法となっている。

文字列の連結
C言語のように文字列同士を空白区切りで記述すると連結される。"foo" "bar""foobar"になる。

Rubyでは""であっても変数の展開は行われない。その種の展開を行うためには""の中で#{}という記法を用いる。するとその内部に記述した部分がRubyのコードとして実行され、その値で置き換えられる。

"foo#{bar}baz" #=> "foo<barの値>baz"
"foo#{1+1}bar" #=> "foo2bar"

なお、文字列ではないが、Rubyでは正規表現に対する文法上のサポートがあり、正規表現を//で表現できる。%r!!のような記法も使用できる。表規表現中では#{}の展開も行うことができる。

数値

PHP、Rubyともに基本的な記法は同じである。

PHPの整数型データはアーキテクチャのワードサイズに依存する。

0x7FFFFFFF   // int(2147483647)
0x7FFFFFFF+1 // float(2147483648)
1073741824*1073741824 // float(1.15292150461E+18)

整数および不動小数点数ともに、組み込みのデータ型で必要な精度が得られない場合にはGMPモジュールを使用する。

Rubyの整数オブジェクトにはFixnumとBignumの二種類があり、アーキテクチャのワードサイズの1/4(32bitなら-2^30〜2^30-1)はFixnum、それ以上はBignumとなる。これらは自動的に変換される。

1073741824*1073741824 #=> 1152921504606846976

不動小数点数のサポートもあるが、必要な精度が得られない場合にはBigDecimalモジュールを使用する。

論理値

PHP、Rubyとも真偽値はtruefalseで表現する(PHPは大文字小文字を無視する)。

PHPではコンテキストによりtrue1false0の変換が行われる。

true + true; // 2

また0''nullfalseは偽として、trueを含むそれ以外の表現は真として扱われる。

Rubyではfalsenilが偽、それ以外が真として扱われる。0''も真である。

配列

PHPではいわゆる配列と連想配列の両方を配列型データで扱うことができる。PHPの配列型はハッシュ的な構造に加え、順序を保存するリストを持っている。

要素へのアクセスキーは整数型または文字列型のデータを用いる。具体的なキー指定方法については以下の通りである。

RUbyでは、いわゆる配列はArrayオブジェクト、連想配列はHashオブジェクトでそれぞれ扱う。クラスは異るが各要素へのアクセスキーの指定にはいずれも[]を用いる。

対応するキーがない場合には配列やハッシュのオブジェクトを生成したときに指定したデフォルト値が返されるが、特に指定しなければnilが返されるため、PHPのような明示的な初期化をともなわない配列へのアクセスのようなことはできない(デフォルト値を適切に与えることなどで同様のこ効果は実現できる)。

実行制御

条件分岐

PHPの条件分岐はifで記述する次の形になる。

if (<式>)
  <文>
else if (<式>)
  <文>
else
  <文>

<文>が複数の場合は {...} でブロックを作る。

Rubyの条件分岐もifで、次のような形になる。

if <式>
  <文>
elsif <式>
  <文>
else
  <文>
end

<文>は複数であってもよい。ifも値を返す(最後に実行された文の値を返す)ので次のような書き方もできる。

print if i%2 == 0
        "even"
      else
        "odd"
      end

ifの代わりにunlessを使用すると論理的に逆の条件となる。

多条件分岐

PHPの多条件分岐はswitchで表現する。

switch (<式>) {
case <データ1>:
  <文1>
  break;
case <データ2>:
  <文2>
case <データ3>:
  <文3>
  break;
default:
  <文4>
}

caseに指定するのは実質的には何らかの値である。case節ではbreakにあたるまで実行が続く(<データ2>にあたる場合、<文2>と<文3>が実行される)。

Rubyの多条件分岐はcaseで表現する。

case <式>
when <オブジェクト>, <オブジェクト>, ...
  <文>
when <オブジェクト>, <オブジェクト>, ...
  <文>
else
  <文>
end

条件はwhenで指定する。複数の条件を指定することもできる。when節の<文>の実行は継続されないためPHPのように終了を明示しなくてよい。

なお条件判定には<オブジェクト>===メソッドが使用されるため、文字列や数値に対する一致判定だけでなはえと、多様なオブジェクトによる多様な条件を設定することができる。

繰り返し

PHPでは特定のデータ型によらない条件変数によるループはC言語と同様のスタイルとなる。

for (<式1>; <式2>; <式3>)
 <文>

配列の全要素に対する繰り返しは以下のいずれかの形をとる。

foreach (<配列> as <変数>)
 <文>

foreach (<配列> as <変数1> => <変数2>)
 <文>

一般的な条件による繰り返しはwhileで表現する。

while (<式>)
 <文>

各繰り返しとも中断(break)と新しい繰り返しの開始(continue)ができる。

次にRubyの繰り返しについて。RubyにはPHPのforにあたる記法は文法としては提供されていない。

配列やハッシュに対する繰り返しは以下の形をとる。

for <変数> in <オブジェクト>
  <文>
end

for <変数>, <変数> in <オブジェクト>
  <文>
end

実際にはこの記法はコンテナ系オブジェクト一般に使用できる。また、オブジェクトの内容物に対する繰り返しという点ではfor以外に各クラスで定義されるeach系メソッドが多く用いられる。

条件での繰り返しはwhileまたはuntilで表現する。

while <式>
  <文>
end

until <式>
  <文>
end

各繰り返しとも中断(break)と新しい繰り返しの開始(next)、その繰り返しの再実行(redo)ができる。また、繰り返し全体のやり直し(retry)ができる(retrybeginendでも使用できる)。

関数(メソッド)

PHPの関数

関数呼び出しは関数名に()を付け、その間に引数を書く。

function_name(...)

関数定義は以下のように行う。

function <関数名>(<引数1>, <引数2>=<値>, ...) {
  <定義>
}

デフォルト値を指定した引数(例中の<引数2>)は呼び出し時に省略可能となる。引数はコピー渡しだが、関数定義の引数リストで&を前置した記述をしておくとリファレンスで受けることができる。

関数の戻り値はreturn()で返す。

Rubyのメソッド

メソッド呼び出しはメソッド名に基本的に()を付け、その間に引数を書く。

method_name(...)

ただし引数がなく、かつ、あいまいさがない場合には()を省略できる

メソッド定義は以下のように行う。

def <メソッド名>(<引数1>, <引数2>=<値>, 
            ..., *<引数x>, &<ブロック引数>)
  <定義>
end

デフォルト値を指定した引数(例中の<引数2>)は呼び出し時に省略可能となる。*を前置した引数(<引数x>)は、呼び出し時の引数を配列で受ける。

def foo(a, b=1, *c); end
foo(1)         # a=1, b=1, c=[]
foo(1,2)       # a=1, b=2, c=[]
foo(1,2,3)     # a=1, b=2, c=[3]
foo(1,2,3,4,5) # a=1, b=2, c=[3,4,5]

&を前置した引数(<ブロック引数>)は、呼び出し時のブロックを受けることができる。

def foo(a, b, &block); end
foo(1, 2)          # a=1, b=2, block=nil
foo(1, 2) { <文> } # a=1, b=2
                   # block.callで<文>を評価できる

引数はすべて参照渡しである。そのため渡されたオブジェクト自体を変更するようなメソッド(破壊的なメソッド)を発行すると呼び出し元にも影響が及ぶことになる。

def foo(a, b)
  # オブジェクト自身を変更するメソッド
  # (破壊的なメソッド)
  a.tr!('a-z', 'A-Z')
  # オブジェクトの内容に変換をかけた内容を持つ
  # 新しいメソッドを生成するメソッド
  b.tr('a-z', 'A-Z') 
end
x, y = "foo", "bar"
foo(x, y)

この場合、fooメソッドの戻り値は"BAR"x"FOO"になり、y"bar"のままとなる。

メソッドの戻り値はreturnで返すが、明示されていない場合は最後に評価された式の値が戻り値になる。

エラー処理・例外処理

PHP4はエラーハンドラのみサポートする。エラーの生成は以下のようにする。

trigger_error(<メッセージ>, <タイプ>)

PHP5からtrycatchによる例外処理をサポートした。

try {
   <文>
} catch (<クラス> <変数>) {
   <文>
} catch (<クラス> <変数>) {
   <文>
}

tryブロックで生じた例外はcatchブロックのうちの<クラス>条件にマッチするもので対処することができる。例外の生成はthrowによって行う。

RubyにはPHPのエラーハンドラのような仕組みはなく、すべて例外によって処理する。

begin
  <文>
rescue <クラス> => <変数>
  <文>
rescue <クラス> => <変数>
  <文>
ensure
  <文>
end

beginendで生じた例外は指定された<クラス>条件にマッチするrescue節で対処できる。起きた例外は<変数>で参照できるが、この変数の指定を省略した場合には$!で参照できる。

例外が起きたかどうかによらず実行したい処理はensure節に書く。例外の生成はraiseメソッドによって行う。以下、あまり現実味のない例を示す。

begin
  f = open("/path/to/file", "r")
  begin
    # ファイルに対する処理
    f.each_line do |line|
      ...
    end
  rescue Exception
    # 処理中に何らかのエラーが起きた
    ...
  ensure
    # 開いたファイルはここで閉じるておきたい(とする)
    f.close
  end
rescue
  # ファイルを開けなかった
end

プログラムの終了

PHP、Rubyともに以下のいずれかで終了する。

ただしRubyにはexitメソッドのほかにexit!という別のメソッドがある。両者の違いは以下の通り。

exit(<終了ステータス>)
exit!(<終了ステータス>)

コメント

PHPの一行コメントは以下のように記述する。

// コメント

# コメント

Rubyの一行コメントは以下のように記述する。

# コメント

PHPの複数行コメントは以下のようにC言語と同じスタイルで記述する。

/* ... 
   ...
   ... */

Rubyの複数行コメントは以下のように行頭からの=beginと行頭からの=endではさむ形で記述する。ただしこの記法はRDドキュメントを埋め込むためにも使われることがある。

=begin
...
...
...
=end

この文書について

参考文献

プログラミングRuby 第2版 言語編(Dave Thomas/Chad Fowler/Andy Hunt)プログラミングRuby 第2版 言語編
はじめてのPHP言語プログラミング入門(大垣 靖男)はじめてのPHP言語プログラミング入門

著者