24
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[PHP] Trait の private は使用しているクラス上では普通に使える件

Posted at

マジで?って思ったので

皆さんこんにちは
PHPで実装するとき、当然のようにメソッドにアクセス権をつけるわけです。
公開するpublic, 自身、および継承先の中でのみ使用できるprotected, 自身の中でしか使えないprivate...。
今更何いってんだって思うかもしれんのですが、このアクセス権がいい感じに作用しないケースがあったので、簡単にまとめます。

名前のセンスとか、気にしちゃだめよ?

バージョン

PHP 7.2.4 です。

trait

traitはコードを再利用する仕組みの一つです。
個人的なイメージは「特定の処理のまとまりをお手軽に導入する仕組み」となっています。

使い方は簡単で

Operation.php
<?php
namespace Niisan;

trait Operation
{
    public function go() :string
    {
        return 'go';
    }
}

というtraitを作ると

User.php
<?php
namespace Niisan;

class User
{
    use Operation;
}

みたいなクラスを作ったときに

test.php
<?php
require(__DIR__ . '/../vendor/autoload.php');

$user = new Niisan\User;
echo isOk($user->go() === 'go');

function isOk($state)
{
    if ($state) return "0K\n";
    return "NG\n";
}

を動かすと

$ php test.php
OK

のように実行できます。
つまり、Operation trait にあったコードがそのままUserクラスで使用できるということです。

trait のアクセス権検証

外部からの使用

次に、traitにアクセス権を設定してみましょう。

Operation.php
<?php
namespace Niisan;

trait Operation
{
    public function go() :string
    {
        return 'go';
    }

    protected function wait() : string
    {
        return 'wait';
    }

    private function stop() :string
    {
        return 'stop';
    }
}

こいつを普通に使ってみます。

test.php
echo isOk($user->wait() === 'wait');

でも、これはアクセス権により弾かれます。

Uncaught Error: Call to protected method Niisan\User::wait()

当たり前ですね。
当然、private メソッドでも同じです。

test.php
echo isOk($user->stop() === 'stop');
Uncaught Error: Call to private method Niisan\User::stop()

アクセス権は効いているようです。

内部からの使用

念の為内部から使うことを考えてみます。
まず、以下のようなメソッドを定義して、内部から使用する仕組みを作ります。

User.php
<?php
namespace Niisan;

class User
{
    use Operation;

    public function operate(int $num)
    {
        if ($num == 1) return $this->go();
        if ($num == 2) return $this->wait();
        return $this->stop();
    }
}

テストはこんな感じで作ります。

test.php
echo isOk($user->operate(1) == 'go');
echo isOk($user->operate(2) == 'wait');
echo isOk($user->operate(3) == 'stop');

すると、こんな感じになります。

# php test.php
0K
0K
0K

いや、待って、privateで落ちてくれないの?

とりあえず、trait で private にしたメソッドは、そのtraitを使用しているクラス内では、普通に参照できるっぽいです。

継承先での使用

継承先で使用することを考えてみます。

Boss.php
<?php
namespace Niisan;

class Boss extends User
{
    public function operate(int $num)
    {
        if ($num == 1) return $this->go();
        if ($num == 2) return $this->wait();
        return $this->stop();
    }
}

テストはこんな感じ

$boss = new Niisan\Boss;
echo isOk($boss->operate(1) == 'go');
echo isOk($boss->operate(2) == 'wait');
echo isOk($boss->operate(3) == 'stop');
# php test.php
0K
0K

Fatal error: Uncaught Error: Call to private method Niisan\User::stop() from context 'Niisan\Boss' in /var/www/src/Boss.php:10

エラー出ました。

結論

trait は単純に継承の代わりをしているわけではなく、
「trait の中にある内容を、クラス定義に書き込んでいるのと同等」
であるということらしいです。
なので、trait のなかで private 定義しても、使用しているクラス上では普通に使用できます。

まとめ

というわけで、trait のアクセス権の挙動について、少し調べました。

いや、コードレビューのときに、trait の中のprivate メソッド使っているのテスト通っていたので、なんでやろうなぁ?って思っただけなんです。

24
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?