PHPでのProtectedの活用!初心者が理解するための5つの具体例

PHPでのprotectedキーワードの説明とサンプルコードを表示しているスクリーンショットPHP
この記事は約19分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

PHPの学習を始めたばかりの方に向けたこの記事では、特にオブジェクト指向プログラミングにおけるキーワード「protected」について詳しく解説します。

PHPのprotectedキーワードの役割、基本的な使い方、そして具体的な応用例を5つのサンプルコードを通じて学べます。

記事を最後まで読めば、PHPにおけるprotectedの使い方について理解を深めることができ、自身のコードをより安全かつ効率的に書くスキルを磨くことができます。

●PHPとは

○PHPの歴史

PHPは、1995年にRasmus Lerdorf氏によって開発されました。

当初は「Personal Home Page Tools」という名前でしたが、現在は「PHP: Hypertext Preprocessor」という再帰的な頭字語となっています。

それ以来、PHPはウェブ開発におけるサーバーサイドスクリプト言語として広く採用され、特にWordPressなどのCMSやLaravelといったフレームワークの背骨として知られています。

○PHPの基本的な構造

PHPはC言語のような命令型の構造を持つ一方で、オブジェクト指向の概念もフルにサポートしています。

これにより、PHPは初心者にとって学びやすく、また大規模なアプリケーション開発にも適した言語となっています。

●Protectedキーワードの基本

○Protectedキーワードの役割

PHPにおける「protected」キーワードは、オブジェクト指向プログラミングにおいて重要な役割を果たします。

protectedキーワードが付与されたプロパティやメソッドは、同じクラスやその子クラスからのみアクセス可能となります。

これにより、データの保護とカプセル化を実現し、外部から直接変更されることを防ぎます。

○Protectedキーワードの書き方

PHPにおけるprotectedキーワードの使用法は非常にシンプルです。

プロパティやメソッドの定義の前に「protected」キーワードを記述するだけです。

// PHPのprotectedキーワードの使用例
class SampleClass {
  protected $protectedProperty;

  protected function protectedMethod() {
    //...
  }
}

この例では、$protectedPropertyというプロパティと、protectedMethod()というメソッドがprotectedキーワードによって保護されています。

この2つは、このクラス自体とその子クラスからのみアクセスが可能となります。

●Protectedキーワードの具体的な使い方

○サンプルコード1:クラス内でのProtectedの使用

まずはクラス内でのProtectedの基本的な使用例を見てみましょう。

class Vehicle {
    protected $speed;

    public function setSpeed($speed){
        $this->speed = $speed;
    }

    public function getSpeed(){
        return $this->speed;
    }
}

$car = new Vehicle();
$car->setSpeed(100);
echo $car->getSpeed(); // 100

このコードでは、Vehicleクラスにprotectedなプロパティ$speedを定義しています。

そして公開メソッドを通じてこのプロパティの値を設定(setSpeed)と取得(getSpeed)が可能になっています。

この例から、protectedプロパティはクラス内からは自由にアクセスできることがわかります。

○サンプルコード2:継承とProtected

次に、protectedが継承とどのように関連しているのかを見てみましょう。

class Vehicle {
    protected $speed;

    public function setSpeed($speed){
        $this->speed = $speed;
    }

    public function getSpeed(){
        return $this->speed;
    }
}

class Car extends Vehicle {
    public function doubleSpeed(){
        return $this->speed * 2;
    }
}

$myCar = new Car();
$myCar->setSpeed(100);
echo $myCar->doubleSpeed(); // 200

ここでは、Vehicleクラスを継承したCarクラスを定義しました。

Carクラスは親クラスVehicleのprotectedプロパティ$speedに直接アクセスし、その速度を倍にするdoubleSpeedメソッドを追加しています。

この例から、protectedプロパティは子クラスからもアクセス可能であることがわかります。

○サンプルコード3:ゲッターとセッターの組み合わせ

ゲッターとセッターは、protectedプロパティを外部から安全にアクセスするためのメソッドです。

class Vehicle {
    protected $speed;

    // セッター
    public function setSpeed($speed){
        if($speed < 0){
            echo "Speed must be positive.";
            return;
        }
        $this->speed = $speed;
    }

    // ゲッター
    public function getSpeed(){
        return $this->speed;
    }
}

$car = new Vehicle();
$car->setSpeed(-100); // Speed must be positive.
$car->setSpeed(100);
echo $car->getSpeed(); // 100

この例では、setSpeedメソッド内で速度が正の値であることをチェックすることで、データの整合性を保つ役割を果たしています。

これはゲッターとセッターの一つの典型的な利用方法であり、外部から直接アクセスできないprotectedプロパティを安全に操作するための手段となっています。

○サンプルコード4:ProtectedとPrivateの違い

protectedキーワードとprivateキーワードの違いを理解するためのコード例を見てみましょう。

class Vehicle {
    private $privateSpeed = 100;
    protected $protectedSpeed = 200;

    public function getPrivateSpeed() {
        return $this->privateSpeed;
    }

    public function getProtectedSpeed() {
        return $this->protectedSpeed;
    }
}

class Car extends Vehicle {
    public function displaySpeeds() {
        // echo $this->privateSpeed; // エラーが発生します
        echo $this->protectedSpeed; // 200
    }
}

$myCar = new Car();
$myCar->displaySpeeds(); // 200

このコードでは、Vehicleクラスにprivateプロパティ$privateSpeedとprotectedプロパティ$protectedSpeedを定義しています。

そして、それぞれの速度を取得するための公開メソッドを作成しています。

Vehicleクラスを継承したCarクラスでは、親クラスのprivateプロパティとprotectedプロパティをそれぞれ参照しようとしています。

しかし、子クラスから親クラスのprivateプロパティにアクセスするとエラーが発生します。

これは、privateプロパティが定義されたクラス内からしかアクセスできないからです。

一方、protectedプロパティはその子クラスからもアクセスできるため、問題なく出力できます。

これが、protectedとprivateの違いです。

○サンプルコード5:ProtectedとPublicの違い

次に、protectedキーワードとpublicキーワードの違いを確認します。

class Vehicle {
    public $publicSpeed = 100;
    protected $protectedSpeed = 200;

    public function getProtectedSpeed() {
        return $this->protectedSpeed;
    }
}

$car = new Vehicle();
echo $car->publicSpeed; // 100
// echo $car->protectedSpeed; // エラーが発生します
echo $car->getProtectedSpeed(); // 200

このコードでは、Vehicleクラスにpublicプロパティ$publicSpeedとprotectedプロパティ$protectedSpeedを定義しています。

そして、protectedプロパティの速度を取得するための公開メソッドも作成しています。

publicプロパティはその名の通り、どこからでもアクセス可能です。

なので、クラスの外部から直接値を取得することが可能です。

一方、protectedプロパティはそのクラスまたは継承したクラスの内部からしかアクセスできません。

そのため、直接値を取得しようとするとエラーが発生します。

この違いを理解することで、各キーワードがどのようなシーンで使われるかが分かるでしょう。

●Protectedキーワードの応用例

ここからは、protectedキーワードの具体的な使用例を通じて、その活用方法を理解しましょう。

PHPにおけるオブジェクト指向プログラミングの中心的な概念であるprotectedをどのように使うのか、具体的なコードとともに解説します。

○サンプルコード6:Protectedを活用した設定ファイルの管理

まずは、設定ファイルの管理においてprotectedをどのように使うのか見ていきましょう。

class Config {
    protected $databaseConfig = [
        'hostname' => 'localhost',
        'username' => 'root',
        'password' => 'password',
        'database' => 'my_database',
    ];

    public function getDatabaseConfig() {
        return $this->databaseConfig;
    }
}

class Database extends Config {
    private $connection;

    public function connect() {
        $config = $this->getDatabaseConfig();
        $this->connection = new PDO('mysql:host='.$config['hostname'].';dbname='.$config['database'], $config['username'], $config['password']);
    }
}

$db = new Database();
$db->connect();

このコードでは、Configクラスの中にデータベース接続のための設定を保持しています。

そして、それぞれの設定値はprotected変数$databaseConfigに配列として格納されています。

これにより、この設定はConfigクラスまたはその子クラスからしかアクセスできません。

この設定を取得するための公開メソッドgetDatabaseConfigも定義しています。

DatabaseクラスはConfigクラスを継承し、データベースへの接続機能を実装しています。

設定値は親クラスから取得し、PDOを使ってデータベースに接続します。

protectedを使うことで、設定情報を安全に保護しながら、必要なときには子クラスからアクセスできるという柔軟性を実現しています。

○サンプルコード7:抽象クラスとProtected

次に、抽象クラスとの組み合わせで、protectedを活用する例を見てみましょう。

abstract class Animal {
    protected $name;

    public function __construct($name) {
        $this->name = $name;
    }

    abstract protected function makeSound();
}

class Dog extends Animal {
    protected function makeSound() {
        echo $this->name . " says woof!";
    }
}

$dog = new Dog("Snoopy");
$dog->makeSound(); // Snoopy says woof!

このコードでは、Animalという抽象クラスを定義しています。

そして、それぞれの動物が共通して持つ名前を保持するためのprotected変数$nameを定義し、コンストラクタでその値を設定しています。

また、動物が出す音を表現する抽象メソッドmakeSoundを定義し、これを子クラスで具体的に実装することを強制しています。

DogクラスはAnimalクラスを継承し、makeSoundメソッドを実装しています。

このメソッドではprotected変数$nameにアクセスし、それを使って犬が鳴く音を表現します。

ここでも、protectedを使って親クラスと子クラス間で情報を共有し、一方でその情報を外部から保護する役割を果たしています。

○サンプルコード8:トレイトとProtected

トレイトとProtectedを組み合わせた応用例を次に示します。

トレイトを使うことで、クラス間で共有するメソッドを作り、その中でもprotectedを活用することが可能です。

trait Logger {
    protected function log($message) {
        echo $message;
    }
}

class Application {
    use Logger;

    public function run() {
        $this->log("Application is running...");
    }
}

$app = new Application();
$app->run(); // Application is running...

このコードでは、Loggerという名前のトレイトを定義しています。

その中に、メッセージをログとして出力するためのprotectedメソッドlogを作っています。

このメソッドは、トレイトを使用するクラス内からしかアクセスできません。

Applicationクラスは、Loggerトレイトを使用しています。

そのため、logメソッドを利用してログを出力することができます。

Applicationのインスタンスを作成し、runメソッドを呼び出すと、”Application is running…”というメッセージが出力されます。

この例から、トレイトとprotectedを組み合わせることで、異なるクラス間で安全にメソッドを共有することができることを理解できたでしょう。

○サンプルコード9:名前空間とProtected

次に、名前空間とProtectedを組み合わせたコードを見ていきましょう。

名前空間はクラスや関数、定数などをグループ化するための概念で、同じ名前のクラスが存在しても衝突を避けることができます。

しかし、名前空間が異なるクラス間でのprotectedメンバへのアクセスは制限されることを覚えておきましょう。

namespace App;

class User {
    protected $name;

    public function __construct($name) {
        $this->name = $name;
    }
}

namespace AnotherApp;

class Manager extends \App\User {
    public function showName() {
        echo $this->name; // Error: Cannot access protected property AnotherApp\Manager::$name
    }
}

$manager = new Manager("John");
$manager->showName();

このコードは、AppAnotherAppという二つの名前空間を定義しています。

App\Userクラスでは、$nameというprotectedプロパティを持っています。

しかし、AnotherApp\ManagerクラスがApp\Userクラスを継承しても、$nameプロパティに直接アクセスすることはできません。

これは、Managerクラスが異なる名前空間に属しているためです。

したがって、このコードを実行すると、エラーが発生します。

これは、名前空間が異なると、protectedプロパティやメソッドへのアクセスが制限されることを表しています。

○サンプルコード10:デザインパターンとProtected

最後に、デザインパターンとProtectedを組み合わせた応用例を見てみましょう。ここでは、Singletonパターンを用いた例を表します。

Singletonパターンでは、あるクラスのインスタンスが一つしか存在しないことを保証します。

これを実現するために、コンストラクタをprotectedにして、外部からの直接のインスタンス化を防ぎます。

class Singleton {
    protected static $instance;

    protected function __construct() {}

    public static function getInstance() {
        if (null === static::$instance) {
            static::$instance = new static();
        }

        return static::$instance;
    }
}

$instance1 = Singleton::getInstance();
$instance2 = Singleton::getInstance();

var_dump($instance1 === $instance2); // bool(true)

このコードでは、Singletonクラスを定義しています。

このクラスでは、$instanceというprotected staticプロパティを持っています。

そして、コンストラクタをprotectedにして、外部から直接インスタンス化を防いでいます。

一方で、getInstanceメソッドを通じてSingletonクラスのインスタンスを取得することができます。

このメソッドでは、$instanceがまだ存在しない場合にのみ新しいインスタンスを作成します。

したがって、何度getInstanceメソッドを呼び出しても、同じインスタンスが返されることになります。

このコードを実行すると、$instance1と$instance2は同じインスタンスを指しているため、結果はbool(true)となります。

●Protectedキーワードの注意点と対処法

これまで見てきたように、protectedキーワードは、クラス内部や継承クラスからしかアクセスできないプロパティやメソッドを定義するために使用されます。

これは、クラスの外部からこれらの要素に直接アクセスすることを防ぎ、データの整合性を保つための重要なメカニズムです。

しかし、protectedキーワードを使用する際にはいくつか注意点があります。

一つ目は、継承関係にあるクラスからのみアクセス可能なため、名前空間が異なる場合や、継承関係にない他のクラスからはアクセスできないということです。

二つ目は、protectedメソッドやプロパティをオーバーライドする際には、その可視性を維持するか、公開(public)にする必要があります。

プライベート(private)にすることはできません。これらの注意点を理解しておくことで、protectedキーワードを適切に利用することができます。

●Protectedキーワードのカスタマイズ方法

PHPでは、protectedキーワードを使用してプロパティやメソッドのアクセス範囲を制限することができますが、それだけではなく、より柔軟にアクセス制御を行うための機能も提供しています。

それが「ゲッター(getter)」と「セッター(setter)」です。

ゲッターメソッドは、protectedプロパティの値を取得するための公開メソッドで、セッターメソッドは、その値を設定するための公開メソッドです。

これらのメソッドを使用することで、クラスの外部からプロパティに安全にアクセスすることができ、同時にデータの整合性も保つことができます。

ここでは、その使用例を紹介します。

下記のUserクラスでは、$nameというprotectedプロパティを持っており、このプロパティに対するゲッターとセッターを提供しています。

class User {
    protected $name;

    public function getName() {
        return $this->name;
    }

    public function setName($name) {
        if (!is_string($name) || strlen($name) < 3) {
            throw new InvalidArgumentException("Name is invalid.");
        }

        $this->name = $name;
    }
}

この例では、setNameメソッド内で名前の検証を行っています。

このようにゲッターやセッターを利用すれば、外部からアクセスされる前にプロパティ値の検証や加工が可能となり、より安全なコードを書くことができます。

まとめ

今回は、PHPでのprotectedキーワードの活用方法を5つの具体的なサンプルコードと共に解説しました。

protectedキーワードは、クラス内部や継承クラスからしかアクセスできないプロパティやメソッドを定義するための非常に重要な概念です。

また、ゲッターとセッターを使うことで、より細かいアクセス制御が可能となります。

これにより、データの整合性を保ちつつ、柔軟な操作を可能にします。

これらの概念を理解し、実際のコードに活用することで、より堅牢で可読性の高いPHPのオブジェクト指向プログラミングが実現できるでしょう。

ぜひ、今回の記事を参考に、protectedキーワードの活用を深めてみてください。