tweeeetyのぶろぐ的めも

アウトプットが少なかったダメな自分をアウトプット<br>\(^o^)/

関数の引数の参照渡し(perl&php)

はじめに

phpperlを交互にやってるとたまに混同するのでメモ

参照渡しについて軽く整理します

まずはperlから

perlはサブルーチンへの引数がデフォルトで参照渡しなので
参照渡しのテストスクリプトはこんな感じで確認できます
refer_test.pl

#!/usr/bin/perl

use strict;
use warnings;

# REFER TEST1
my $var1 = 'motono yatu1';
&rewrite1($var1);
print $var1 . "\n";

sub rewrite1
{
  $_[0] = 'rewrite sita yatu';
}

# REFER TEST2
my $var2 = 'motono yatu2';
&rewrite2(\$var2);
print $var2 . "\n";

sub rewrite2
{
  my $arg = shift;
  $$arg = 'rewrite sita yatu';
}
1;

スクリプトたたいた結果

# perl refer_test.pl
rewrite sita yatu
rewrite sita yatu

REFER TEST1 はデフォルトで参照渡しなのでサブルーチンの中で直接引数($_[0])を変更するとそのまま変更されるよ、って例。

REFER TEST2 は受け取り側が局所変数(my $arg)として引数を受け取っているので、いったん$argは局所変数($var2のリファレンス値)として扱われる。
その後、デリファレンス(リファレンスをもとのやつに戻す)したやつ($$arg)を変更しているので、値が変更されます。
(説明がへたですみません)

ちなみに、REFER TEST2 で関数呼び出しを
&rewrite2(\$var2); → &rewrite2($var2);に変えて実行すると下記のように怒られます。
&rewrite2($var2);に変えてスクリプトたたいた結果

# perl refer_test.pl
rewrite sita yatu
Can't use string ("motono yatu2") as a SCALAR ref while "strict refs" in use at refer_test.pl line 28.

phpのほう1

phpのほうは
デフォルトで、関数の引数は値で渡されます、とのことなのでそれはそれで試してみます。
perlと比較するために極力同じ形で記述します。
refer_test.php

<?php
// REFER TEST1
$var1 = 'motono yatu1';
rewrite1($var1);
echo $var1 . "\n";

function rewrite1($arg){
	$arg = 'rewrite sita yatu';
}

// REFER TEST2
$var2 = 'motono yatu2';
rewrite2(&$var2);
echo $var2 . "\n";

function rewrite2($arg){
	$arg = 'rewrite sita yatu';
}
?>

スクリプトたたいた結果

# php -f refer_test.php
PHP Deprecated:  Call-time pass-by-reference has been deprecated in /home/hoge/phptest/refer_test.php on line 13

Deprecated: Call-time pass-by-reference has been deprecated in /home/hoge/phptest/refer_test.php on line 13

motono yatu1
rewrite sita yatu

なんか怒られてますw が、とりあえず置いといて(↓で補足します)、
REFER TEST1 は、渡した変数をそのまま変えても値は変わりませんでした。

REFER TEST2 は、あえて参照を渡して、その値を変えると元の値も変わりました。

phpのほう2

'phpのほう1'は、あえてperlと合わせるために↑のように記載しましたが、正しくはphpでの参照渡しはこう書きます
refer_test2.php

<?php
// REFER TEST1
$var1 = 'motono yatu1';
rewrite1($var1);
echo $var1 . "\n";

function rewrite1(&$arg){ // ←引数の指定のところで、参照を受け取ることを明示する
	$arg = 'rewrite sita yatu';
}

// REFER TEST2
$var2 = 'motono yatu2';
rewrite2(&$var2);
echo $var2 . "\n";

function rewrite2(&$arg){ // ←引数の指定のところで、参照を受け取ることを明示する
	$arg = 'rewrite sita yatu';
}
?>

スクリプトたたいた結果

# php -f refer_test2.php
PHP Deprecated:  Call-time pass-by-reference has been deprecated in /home/hoge/phptest/refer_test2.php on line 13

Deprecated: Call-time pass-by-reference has been deprecated in /home/hoge/phptest/refer_test2.php on line 13

rewrite sita yatu
rewrite sita yatu

こちらも怒られましたが、いったんおいておきますw
関数の引数の指定のところで、参照を受け取るってことを明示しているので
そのまま渡そうが参照を渡そうが、元の値が書き換えられて表示されてます。

怒られてるやつ

ここで、怒られてるやつですが、説明はぐーぐる先生のお言葉を借ります

関数コール時に参照で引数を渡すことは、コードの明解さを損なうために廃止されています。

ということらしいです。
php.iniを確認したら確かに「allow_call_time_pass_reference」がOFFでした。

ということで、まとめ

perl…デフォルト参照渡し。受け取り側(関数定義)で、局所変数を使って引数を受け取る場合は、それ以降の処理は渡し方と合わせる必要がある。
php…デフォルト値渡し。受け取り側(関数定義)で、引数の型を指定する。渡すほうは原則参照渡しを行わない。

時間の都合と、これ以上は長くなるので、
次に変数代入の参照渡しと関数戻り値の参照渡しについても試してメモりたいと思います。






怒られたやつを念のためメモ
http://blog.livedoor.jp/fbikqtfm/archives/50807718.html

Call-time pass-by-reference has been deprecated
Call-time pass-by-reference has been deprecated

というエラーが出てきたので調べてみたところ、

php.iniの「allow_call_time_pass_reference」がOFFの場合のエラーなようです。


自分の環境では、この値が「OFF」になっていたんですね。
それを「On」にすることで、このエラーは出なくなりました。

エラーのないようですが、phpマニュアルに


 関数のコール時に引数が参照で渡された場合に、警告するかどうかを 設定します。
 この機能は過去のものであり、将来のバージョンの PHP/Zend では
 サポートされない可能性があります。
 推奨される方法は、関数宣言時に参照渡しとするべき引数を指定する ことです。
 将来のバージョンでの動作を保障するために、この オプションを off とし、
 スクリプトがこの状態で正しく動作することを 確認することが推奨されます
 (この機能を使用する度に警告が発生します)。

 関数コール時に参照で引数を渡すことは、コードの明解さを損なうために
 廃止されています。関数は、引数が参照渡しであると宣言されて いない場合でも、
 文書化されていない方法で、その引数を修正できます。
 副作用を回避するためには、どの引数を参照渡しとするかを関数宣言でのみ
 指定すると良いでしょう。

とありますので、ソースコードの見直しをした方がいいのでしょうね。