見出し画像

PHPで少し驚いた、他の言語とは違う言語仕様

最近PHP関連のことを調べていると、やっぱりPHP嫌いのエンジニアの方が多いせいか、アンチ記事なんかもちょこちょこ目立ちます。

自分はPHPを業務で使った経験は少しだけしかありません。普段はPythonで仕事をしており、プライベートで動かしているWEBサービスがLaravel PHPで開発しているため、現在はそこでのみPHPを使用しています。

PHP嫌いの方や、PHPを使いこなしている方には、もしかしたら常識的な知識なのかもしれませんが、個人的にPHPの驚いた言語仕様があったため、それについて自分なりにまとめてみました。

自分が実験したPHPは以下のような環境です

$ php -v
PHP 5.5.9-1ubuntu4.22 (cli) (built: Aug  4 2017 19:40:28) 
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies
    with Zend OPcache v7.0.3, Copyright (c) 1999-2014, by Zend Technologies

参照渡しをすると元の配列に影響を及ぼす

最初は配列周りの部分の言語仕様の例です。
例として以下のようなPHPのコードがあるとします。

<?php
// test.php

$array = array(1,2,3); 
$ref = &$array[1];

$copy = $array;
$copy[0] = 'a';
$copy[1] = 'b';
$copy[2] = 'c';

print_r($array);

?>

ここで配列である$arrayを標準出力するprint_rにはどんな値が入るでしょうか?

これは普段どんな言語を使い慣れているかで、人によって期待する結果は違うと思います。ちなみに上記の結果は以下のようになります。

$ php test.php
Array
(
   [0] => 1
   [1] => b
   [2] => 3
)

自分はこの仕様に戸惑いを感じました。
ちなみに自分が普段使い慣れているPythonで同じようなコードを書くと以下の結果が出力されます。

// test.py

array = [1,2,3]
val = array[1]

copy = array
copy[0] = 'a'
copy[1] = 'b'
copy[2] = 'c'

val = 10

print(array)

# print(array)の標準出力結果
# ['a', 'b', 'c']

プログラミング言語によっては[1,2,3]と表示されるものあれば、Pythonのように['a','b','c']と表示されるものもあります。
Pythonの場合は変数に別な配列の変数を代入すると、代入された変数(上記の場合はcopy)は、代入した配列(上記の場合はarray)を参照するようになります。
そのため、copyの配列の内容を入れ替えれば、arrayの実体を参照しているため、arrayの配列の中身も変わります。これはRubyでも同様です。

array = [1,2,3]
val = array[1]
copy = array
copy[0] = 'a'
copy[1] = 'b'
copy[2] = 'c'
array.each{|x| print "#{x}\n";}

# 標準出力結果
# a b c

もう少しPHPの配列周りがどうなっているかを調べてみましょう。
以下のようなコードを実行してみます。

<?php
   $buffer = array();

   function test( &$ary ){
       global $buffer;
       for( $i = 0 ; $i < sizeof($ary) ; $i++ ){
           if( $ary[$i] == 2 ){
               $buffer[] = &$ary[$i];
           }
       }
   }

   $array = array(1,2,3);

   var_dump($array);
   test( $array );
   var_dump($array);

上記を実行してみると以下のような結果が実行されます。

array(3) {
 [0]=>
 int(1)
 [1]=>
 int(2)
 [2]=>
 int(3)
}
array(3) {
 [0]=>
 int(1)
 [1]=>
 &int(2)
 [2]=>
 int(3)
}

注目すべきはtest関数を呼び出す部分のコードです。
test関数に$array配列を与えた後、var_dumpで$arrayの中身を見てみると、$arrayの二番目が&int(2)となりました。

test関数をもう少し詳しく見てみると、array配列の2の値がある番地で、$buffrerという新たな配列に、$ary[$i]にリファレンス演算子&をつけて変数を渡しています。
これにより、$ary[1]はリファレンスモードになります。

   function test( &$ary ){
       global $buffer;
       for( $i = 0 ; $i < sizeof($ary) ; $i++ ){
           if( $ary[$i] == 2 ){
               $buffer[] = &$ary[$i];
           }
       }
   }

$ary[1]の実体である値2は別な場所に値をコピーされ、$ary[1]の中身自体には実体である値2を指すポインタに変換されます。

ここで新たな関数hogeを加えた上記のコードを改修した以下のようなコードを実行してみます。

<?php
   $buffer = array();
   function foo( &$ary ){
       global $buffer;
       for( $i = 0 ; $i < sizeof($ary) ; $i++ ){
           if( $ary[$i] == 2 ){
               $buffer[] = &$ary[$i];
           }
       }
   }

   function hoge( $ary ){
       for( $i = 0 ; $i < sizeof($ary) ; $i++ ){
           $ary[$i] += 10;
       }
       return $ary ;
   }

   $array = array(1,2,3);

   foo( $array );
   $copy = bar( $array );

   print_r($copy);
   print_r($array);

上記を実行すると、以下のような結果が表示されます。$copyには期待通りの結果である[11,12,13]と表示されるものの、$array[1]の部分がリファレンスモードとなっているため、$array[1]が12となり、$arrayの中身にも影響が出てしまいます。

Array
(
    [0] => 11
    [1] => 12
    [2] => 13
)
Array
(
   [0] => 1
   [1] => 12
   [2] => 3
)

個人的に上記のようなPHPの言語仕様はあまり好きではありません。書き方次第では、配列の中身が変わってしまい、意図しない動作であるバグを生み出す原因にもなるような気がしてしまいます。

配列のキーが暗黙的に整数型にキャストされてしまう

これはプログラマによっては便利という人もいれば、混乱を招くから好きではないという人もいると思います。

どういうことかというのを説明するよりも、まずは実際に動かしてみましょう。以下のようなコードがあるとします。

<?php
   $test = [
       '1' => 'one',
   ];
   
   $hoge = [
       1 => 'one',
   ];

   print_r($test);
   print_r($hoge);

   var_dump($test === $hoge);

結果はどうなるかというと、以下のように表示され、var_dumpの内容はtrueと表示されて、どちらも等価(同じ)であるとみなされます。

Array
(
   [1] => one
)
Array
(
   [1] => one
)
bool(true)

$testのキーである'1'は整数値である1にキャスト(変換)されてしまいます。他にも浮動小数点型(float),論理型(bool)などで実験してみても、以下のように整数値にキャストされてしまいます。

<?php

$test = [
   1.1 => 'test'
];
$hoge = [
   true => 'hoge'
];

print_r($test);
print_r($hoge);

# 標準出力結果
# Array
# (
#     [1] => test
# )
# Array
# (
#     [1] => hoge
# )

ここで、整数型(int)と浮動小数点型(float)や文字列型(string)と論理型(bool)を以下のように配列の中に混ぜると結果はどうなるでしょうか?

<?php

$hoge = [
   1.1 => 'hoge',
   "1" => 'test',
   1 => 'hugu',
   true => 'true'
];

print_r($hoge);

結果は以下のように最後に書いた内容が上書きされます。

Array
(
   [1] => true
)



とりあえずPHPをなんとなくと使っていましたが、最近調べていてPHPで個人的に混乱した言語仕様は以上になります。他の方の記事やブログでも仰っている仕様はいくつかありますが、今回は二つだけ上げてみました。

他にもメジャーな他言語にはあるものの、PHPに無いものとしてよく言われるのは、配列(List)と連想配列(Map)は同じものだったりすることです。

最後に個人的にPHPについて感じること

ここまで記事に書いておいてなんですが、上記のような仕様だったり、文法的に書きづらいという理由もあり、個人的にPHPはあまり好きではない言語ではありません。しかし、「PHPはクソ言語だ」とは思っていません。

自分は上記のような使用なども、”そういうもの”だと認識しているのと、PHPは「Webアプリケーション開発に特化」や「誰でも書けること」をコンセプトとしているからです。自分が好きなPythonでは「書きやすさ」という意味では共通している思想でもあります。

よくPHPをやっている方に対して言われるのに「PHPプログラマはレベルが低い」などと言われる方がいますが、これだけ使用する人が多い言語だと、能力にバラツキが生まれてしまうのは仕方ありません。自分の尊敬しているエンジニアの一人にPHPを使っている方もいますし、”PHPを使っているからレベルが低い”とは限らないと思います。

近々、PHPの歴史、PHPがここまで非難されてしまった経緯について考察した記事を書こうと思います。

参考資料

ほとんど以下の方々のコードを参考(パクリ)にしました



この記事が気に入ったらサポートをしてみませんか?