PHPのオブジェクトと参照渡し

2015年1月27日(火)
原田 裕介(はらだゆうすけ)

こんにちは。ついに第4回目となりました、今回は、オブジェクトと参照渡しについてです。

オブジェクト指向を理解されている方には簡単だと思われるオブジェクトの扱いですが、PHP技術者認定試験では「これどうなるんだっけ?」と思うような書き方のプログラムがよく出題されます。

オブジェクトの振る舞いを踏まえて、実行結果がどうなるか問うようなパターンが多くみられるため、似たような出題のされ方をする参照渡しと合わせて見ていきましょう。

参照渡し

通常では関数の引数として渡した変数の値を関数の中で変化させても、元の変数には変化がありません。例を見てみましょう。

4_1.php

<?php
function sampleFunction($foo){
  $foo++;
}

$bar = 1;
sampleFunction($bar);

echo $bar;

出力結果

>php 4_1.php
1
4_1.php

図1:4_1.php

ですが、関数を定義する際に&(アンパサンド)を引数の前につけることで、関数の中から関数の外にある変数の値を変化させることができます。

4_2.php

<?php
function sampleFunction(&$foo){
  $foo++;
}

$bar = 1;
sampleFunction($bar);

echo $bar;

出力結果

>php 4_2.php
2
4_2.php

図2:4_2.php

4_2.phpのような使い方を、参照渡し(またはリファレンス渡し)と言います。通常だと、関数に渡された引数に関数内で別の値が代入されても、関数外の変数には影響がありません。
変数にはそれぞれ有効範囲(スコープ)があり、その有効範囲外では利用できないからです。そのため、関数の外で定義された変数を、関数の中で利用しようと思ってもできません。ただPHPでは定義していない変数は空の変数として扱うことができるので、正確には変数自体は利用できますが、空の状態になります。

一般的に、PHPでは関数に渡した引数は変数そのものではなく、値が渡されます。そのために、関数内で別の値が代入されても関数外の変数には影響がないのです。

PHP技術者認定上級試験では、こういった参照渡しを使って「書き方的に問題がないか」、「変数がどのように扱われるか」といったことを問う問題がしばしば出題されます。前者は、参照渡しにもいくつか書き方のパターンがありますので、どういった書き方があるのかを理解しておくと良いでしょう。後者については、前者を理解した上でしっかりコードを読めば、おのずと答えが出るはずです。

それではこういった書き方はどうでしょう。

4_3.php

<?php
function sampleFunction($foo){
  $foo++;
}

$bar = 1;
sampleFunction(&$bar);

echo $bar;

出力結果

>php 4_3.php

Deprecated: Call-time pass-by-reference has been deprecated; If you would like to pass it by reference, modify the declaration of sampleFunction().  If you would like to enable call-time pass-by-reference, you can set allow_call_time_pass_reference to true in your INI file in D:\xampp\htdocs\test\php_col\4\4_3.php on line 7
2

4_3.phpの例のように、関数呼び出し時に参照渡しをする方法は、以前は良く使われていました。特にPHP4の頃は、頻繁に使われていたように感じます。

このように呼び出し時に参照渡しをする方法を、“call-time pass-by-reference”と呼びます。この方法はPHP5.3では非推奨となり、PHP5.4からは機能自体が削除されているため、使えなくなっています。ただ、現在のPHP技術者認定試験ではPHP5.3をベースとして出題されているため、「非推奨のエラーが出るが使える」ということを覚えておきましょう。

参照については、これ以外に関数の戻り値自体を参照で返すという書き方もあります。

4_4.php

<?php
class test{
  $foo = 1;
  function &getFoo(){
    return $this->foo;
  }
}

$test = new test();
$result =& $test->getFoo();
$test->foo = 5;

echo $result;

出力結果

>php 4_4.php
5

これを「リファレンス構文」といいます。リファレンス構文でない普通の関数は、毎回コピーを作成するためメモリの無駄であるとして、リファレンス構文をオススメする人もいます。しかし、PHPマニュアルにも「パフォーマンスを向上させるためだけの目的でこの機能を用いることはやめてください。そのようなことをしなくても、PHPエンジンが自動的に最適化を行います。」と書いてありますので、こういった考えで、リファレンス構文を使うのは間違いです。

ただし、試験では実行結果が正しいかどうかを問われることが多いため、適切かどうかはともかくとして、動くということは認識しておく必要があるでしょう。

さきほど変数にはスコープがあるため、関数内の変数に値を代入しても関数外の変数には影響がないと言いましたが、PHPにはスコープの範囲を広げる方法が用意されています。それが、globalという修飾子です。

global $bar;

上記のように使用すると、その変数のスコープをPHPプログラム全体に広げます。

4_5.php

<?php
function sampleFunction(){
  global $bar;

  $bar++;
}

$bar = 1;
sampleFunction();

echo $bar;

出力結果

>php 4_5.php
2

こういった参照渡しによって変数にどう影響があるか、global修飾子によってスコープがどのようになるかは把握しておきましょう。

著者
原田 裕介(はらだゆうすけ)
株式会社ユーザーローカル
東京都在住。携帯のホームページ作成サービスでHTMLのコーディングを始めて、PHPerとなる。
株式会社ハッシュシステム代表取締役やtetolの立ち上げ、株式会社イードでニュースメディアの開発やエンジニア採用を経て、現在はアクセス解析ツールの開発を行いながら、個人でもWEBサービスの開発を行っている。PHP技術者認定上級試験の認定者でもあり、受賞歴はmixi scrap challenge優勝など。

 

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています