PHP - OOP - 4. アクセス権

続いてプロパティやメソッドを修飾するアクセス権について取り上げます。

アクセス権の記述

アクセス権とはプロパティやメソッドを修飾するキーワードで、外部のPHPプログラムからアクセス可能かどうかを決定するものです。

class MyClass
{
    public $myProperty;

    public function myMethod($x)
    {
        echo $this->myProperty . " " . $x;
    };
}

たとえば上記の MyClass クラスであれば $myProperty プロパティと myMethod メソッドには public というアクセス権を指定してます。この public というアクセス権は外部のPHPプログラムからアクセス可能であることを意味します。

PHPで利用可能なアクセス権には以下の3種類があります。

アクセス権 意味
public どこからでもアクセス可能
protected そのクラス自身、そのクラスを継承したクラス
(および親クラス)からのみアクセス可能
private そのクラス自身からのみアクセス可能

もし先ほどのプログラムの $myProperty プロパティのアクセス権に private を指定すると以下のようなプログラムを実行できなくなります。

require_once("MyClass.php");

$myClass = new MyClass();
$myClass->myProperty = "Hello"; #=> Fatal error

このように privateprotected といったキーワードを指定することで外部のPHPプログラムからのアクセスを禁止できます。

どのアクセス権を選ぶかは開発するプログラムの要件によって決まります。一般的には、プログラムの保守性を高めるためにアクセス権はなるべく狭くしておきます。 public キーワードの利用は必要最低限にとどめておくと良いでしょう。

PHPプログラムの開発(アクセス権の変更)

ここでは簡単な計算機クラス( SimpleCalc クラス)に定義済みの $number プロパティについて、アクセス権を public から private に変更してみましょう。

<?php
class SimpleCalc
{
    private $number;

    public function add($x)
    {
        $this->number = $this->number + $x;
    }

    public function show()
    {
        echo $this->number . PHP_EOL;
    }
}

また実行プログラム( calc_runner.php )は前回と同じものを利用しましょう。

<?php
require_once("SimpleCalc.php");

$calc = new SimpleCalc();

$calc->number = 10;

$calc->add(20);
$calc->add(30);
$calc->show();

プログラムを実行すると次のようなエラー( Fatal error )が出力されます。

$ php calc_runner.php
Fatal error: Uncaught Error: Cannot access private property SimpleCalc::$number in /Users/your_name/Desktop/code-php-db/calc_runner.php:6
Stack trace:
#0 {main}
  thrown in /Users/your_name/Desktop/code-php-db/calc_runner.php on line 6

このように SimpleCalc クラスの $number プロパティのアクセス権を private に変更したことで、外部のPHPプログラムから $calc->number = 10; を実行するとエラーになります。

PHPプログラムの開発(アクセサメソッド)

ここまでのプログラムで $number プロパティへの直接的な値の代入を禁止することができました。次に、開発の現場でよく使うアプローチとしてアクセサメソッドという手法を紹介します。計算機クラス( SimpleCalc クラス)に以下のようにメソッドを追加します。

<?php
class SimpleCalc
{
    private $number;

    public function setNumber($number)
    {
        $this->number = $number;
    }

    public function getNumber()
    {
        return $this->number;
    }

    public function add($x)
    {
        $this->number = $this->number + $x;
    }

    public function show()
    {
        echo $this->number . PHP_EOL;
    }
}

ここでは private$number プロパティにアクセスするための getNumbersetNumber と2つのメソッドを定義しています。 getNumber メソッドは $number プロパティの値を戻り値で返し、 setNumber メソッドは $number プロパティに引数で受け取った値を保存します。このようなプロパティにアクセスするためのメソッドをアクセサメソッドやGetter/Setterメソッドなどと呼びます。

続いて実行プログラム( calc_runner.php )の中から実際にアクセサメソッドを呼び出してみましょう。

<?php
require_once("SimpleCalc.php");

$calc = new SimpleCalc();

// $calc->number = 10;
$calc->setNumber(10);

$calc->add(20);
$calc->add(30);
$calc->show();
echo $calc->getNumber() . PHP_EOL;

これまでは $calc->number = 10 のようにプロパティに直接値を代入していましたが、ここでは $calc->setNumber(10) メソッドを呼び出すことで $number プロパティに値を代入するようにしています。同様に $number プロパティの値を取得したい場合も $calc->getNumber() のようにアクセサメソッドを呼び出すようにします。

それではコマンドラインからプログラムを実行してみましょう。

$ php calc_runner.php
60
60

実行結果から $calc->show() メソッドによる出力と $calc->getNumber() メソッドの戻り値の出力を確認できます。

参考:カプセル化

このようにプロパティのアクセス権を private として外部からの直接的なアクセスを禁止し、メソッド呼び出しを通じて間接的にプロパティを制御することをカプセル化と呼びます。

今回の計算機プログラムにおいては、アクセサメソッドを提供することにあまりメリットが感じられなかったかもしれません。しかしアクセサメソッドを経由してプロパティにアクセスすることで、次のようなプログラミングも可能となります。

    public function setNumber($number)
    {
        if (is_numeric($number)) {
            $this->number = $number;
        }
    }

ここでは setNumber メソッドに if 文による引数のチェックを追加しています。 is_numeric 関数は引数の値が、数字または数値形式の文字列であるかを調べて論理値を返却します。このように実装することで、外部のプログラムから $number プロパティに代入する値を数字(あるいは数値形式の文字列)に限定できます。このようなアクセサメソッドを提供することでプロパティへの不正な値の代入を禁止できます。

まとめ

  • アクセス権には publicprotectedprivage の3つがある
  • アクセス権によって外部のプログラムからプロパティやメソッドへのアクセスを制限できる
  • プロパティやメソッドにアクセス権を定義できる