質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.96%

parent:: で親クラスのインスタンスメソッドと静的メソッドの両方にアクセスできるのが腑に落ちません

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 2,840

7968

score 243

サブクラスから parent:: で親クラスのインスタンスメソッド、静的メソッドの両方にアクセスできます。

<?php

class A
{
    protected $firstName;
    protected $lastName;

    public function getName($args1, $args2) {
        $this->firstName = $args1;
        $this->lastName = $args2;
        echo $this->firstName . $this->lastName;
    }

    protected static $staticFirstName;
    protected static $staticLastName;

    public static function getStaticName($args1, $args2) {
        self::$staticFirstName = $args1;
        self::$staticLastName = $args2;
        echo self::$staticFirstName . self::$staticLastName;
    }
}

class B extends A
{
    public function hoge($args1, $args2)
    {
        parent::getName($args1, $args2);
    }

    public function fuga($args1, $args2)
    {
        parent::getStaticName($args1, $args2);
    }
}

$b = new B();
$b->hoge('武蔵','宮本'); // 武蔵宮本
$b->fuga('小次郎','佐々木'); // 小次郎佐々木

これが腑に落ちません。

インスタンス、静的という考え方に誤りがあるのでしょうか。

static のあるプロパティとメソッドはクラスに属することになり、スコープ定義演算子 :: でアクセスします。(インスタンス化なしでアクセスできる)

static のないプロパティとメソッドはインスタンスに属することになり、アロー演算子 -> でアクセスします。

上記のように私の中では理解しております。

親クラスのインスタンスメソッドにアクセスするなら parent-> 、静的メソッドにアクセスするなら parent:: という記述なら腹落ちしますが、parent:: でインスタンスメソッドと静的メソッドの両方にアクセスできることが腑に落ちません。

なぜ、parent:: で親クラスのインスタンスメソッドと静的メソッドの両方にアクセスできるのでしょうか?

私が根本的に誤って理解しているのかと思います。

どこに誤りがあるのか教えてください((_ _ (´ω` )

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+4

サブクラスから parent:: で親クラスのインスタンスメソッド、静的メソッドの両方にアクセスできます。

違うなぁ。混乱しやすいのかもしれないけど。
インスタンス(動的プロパティ、動的メソッド)にアクセスするのはアロー演算子、
クラス(静的プロパティ、静的メソッド)にアクセスするのがダブルコロン。

お約束のリファレンスより。

PHP: スコープ定義演算子 (::) - Manual
self:: で自クラスのプロパティ、静的メソッドにアクセスできる。
parent:: で親クラスのプロパティ、静的メソッドにアクセスできる。

PHP: アクセス権 - Manual

public 宣言されたクラスのメンバーには、どこからでもアクセス可能です。
protected 宣言されたメンバーには、 そのクラス自身、そのクラスを継承したクラス、および親クラスからのみアクセスできます。
private 宣言されたメンバーには、そのメンバーを定義したクラスからのみアクセスできます。 

PHP: static キーワード - Manual

クラスプロパティもしくはメソッドを static として宣言することで、 クラスのインスタンス化の必要なしにアクセスすることができます。 static なプロパティは、インスタンス化されたクラスオブジェクトから アクセスすることはできません (static なメソッドにはアクセスできます)。

PHP: オブジェクトの継承 - Manual

クラスを拡張するとき、サブクラスは親クラスから public と、protected のメソッドをすべてを引き継ぎます。

すっごく時間をかけて説明文を書いてみたけど、
説明が危うく拙い気がしたので、消して、
もうちょっとわかりやすい記事を探してみた。

PHPを愛する試み 〜self:: parent:: static:: および遅延静的束縛〜 - maeharinの日記
PHPの「$this」と「self::」の違い - Qiita


検証コードを書きながら、
親クラスからの継承の話と、
おなじ命名空間での他クラスからのアクセスの話も知っといたほうがいいと思えてきました。

謎なコードを示します。

<?php

class foo {
    public function hoge() { echo 'hoge'; }
    public static function fuga() { echo 'fuga'; }
}

class bar {
    public function piyo() {
        echo foo::hoge();
    }
}
$x = new bar();
$x->piyo();

これ、実行すると、
「Deprecated: Non-static method foo::hoge() should not be called statically」
ってエラーメッセージが出ますが、
「hoge」って出力されます。

class bar {
    public function piyo() {
        echo foo::fuga();
    }
}


とすることでエラーメッセージなしに「fuga」って出力されます。
クラスfooとbarの間には継承の関係はありません
同じ命名空間にいるだけです。

<?php

class foo {
    public const X = 1;
    public static $y = 2;  // staticがないとFatal error
}

class bar {
    public function piyo() {
        echo foo::X . PHP_EOL;
        echo foo::$y . PHP_EOL;
    }
}
$x = new bar();
$x->piyo();


結果は「1(改行)2(改行)」です。
クラスうんぬんいうまえに、スコープ演算子と命名空間のことも知っとかないと、
危ういと思います。
(これが理解できると、フレームワークのソースコードも読めるようになるのかも。)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/12/12 16:53

    静的メソッドじゃないのに $classname:: で呼び出しできちゃうっていう問題はPHPがいい加減なだけ
    ちなみに静的メソッドを $instance-> で呼び出すこともできるてしかもこっちは注意文も出ない
    PHP7.0でも直さんかったってことはもう直らんのじゃないかな

    https://stackoverflow.com/questions/3754786/calling-non-static-method-with-double-colon

    キャンセル

  • 2018/12/12 17:03

    まぁ、美しい言語じゃなくて、実利をとってる感じはする。

    キャンセル

  • 2018/12/13 11:34 編集

    > m6u さん
    追記と参考サイトありがとうございまいました。
    PHP7 では $this::foo()->foo()::foo() のようなアクセスもエラーなしでできるようで(やらないのが無難だと思いますが... 実行結果:https://3v4l.org/1dTbq)、-> と :: についてちゃんと理解できていないので、実際に試して実行結果から学びたいと思います。
    何度も返信いただきありがとうざいました。

    > KazuhiroHatano さん
    参考サイトありがとうございました。

    キャンセル

checkベストアンサー

+3

staticと、static::は別物です。

static::もparent::もクラスを返します。
$this->の代わりにself::としても動作します。


改めて。
スコープ定義演算子 (::) のマニュアルの

「ダブルコロン」は、トークンのひとつです。 static, 定数 およびオーバーライドされたクラスのプロパティやメソッドにアクセスすることができます。

の「オーバーライドされたクラスのプロパティやメソッド」の部分は正確でないのではないかと考えるに至りました。
マニュアル自身、クラスの基礎の「例2 $this 疑似変数の例」において、A::foo();という書き方で、クラスAのstaticでもオーバーライドされてもいないメソッドfooにアクセスする例を書いてます。
プロパティについて言えば逆にオーバーライドしていてもstaticでないとparent::によって参照することができませんでした。

<?php
class A {
    public function a() { return 1; }
    public $a1 = "prop";
}
class B extends A {
    public function b() { return parent::a(); }
    public function b1() { return parent::$a1; } //PHP Fatal error:  Uncaught Error: Access to undeclared static property: A::$a1
    public $a1 = "new";
}

現在の私の理解としては

「ダブルコロン」は、トークンのひとつです。 「定数」または「staticなクラスのプロパティ」、「クラスのメソッド」にアクセスすることができます。

ではないかと思います。
また、質問者様ご提示のような書き方をしたときに(parent->でなく)parent::で動作する理由のもう一つとして、$this疑似変数が、(呼ばれ方にかかわらず)呼び出し元のオブジェクト指すことも影響しています。parent::getName()として、AのgetNameが呼ばれたときその関数本体の中で使われる$thisは、->を使っていないにもかかわらず、getNameを呼んだ(Aのサブクラスの)インスタンスを適切に指してくれます。詳しくは、上に引用した"クラスの基礎"の説明および例をご参照ください。

こう書いてしまうとやや余談になりますが、先に書いたself::についてはこれもstaticでない自クラスのメソッドを($this->でなくself::で)呼べるという意図でした。下記のパターンもご参考としていただければと思います。

class A {
    public function a() { return 1; }
}
class B extends A {
    public function b() { return parent::a(); }
}
class C extends A {
    public function c() { return A::a(); }
}
class D extends A {
    public function d() { return $this->a(); }
}
class E extends A {
    public function e() { return self::a(); }
}
// class F {
//     public function f() { return A::a(); } //fatal error
// }
$b = new B();
$c = new C();
$d = new D();
$e = new E();
//$f = new F();
// A::a(); // fatal error
var_dump($b->b());
var_dump($b->b1());
var_dump($c->c());
var_dump($d->d());
var_dump($e->e());
//var_dump($f->f());

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/12/12 17:13

    正しいというか、そういう風な動作にしか見えないです。

    キャンセル

  • 2018/12/13 11:34 編集

    なるほど、静的でも継承して同名で定義すればオーバーライドになるのですね。
    PHP7 では $this::foo()->foo()::foo() のようなアクセスもエラーなしでできるようで(やらないのが無難だと思いますが... 実行結果:https://3v4l.org/1dTbq)、-> と :: についてちゃんと理解できていないので、実際に試して実行結果から学びたいと思います。
    何度も返信いただきありがとうざいました。

    キャンセル

  • 2018/12/13 13:24

    こちらこそ勉強になりました。ありがとうございます

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.96%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • トップ
  • PHPに関する質問
  • parent:: で親クラスのインスタンスメソッドと静的メソッドの両方にアクセスできるのが腑に落ちません