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

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

新規登録して質問してみよう
ただいま回答率
85.50%
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

2回答

12322閲覧

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

7968

総合スコア253

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

3グッド

1クリップ

投稿2018/12/11 09:14

サブクラスから 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:: で親クラスのインスタンスメソッドと静的メソッドの両方にアクセスできるのでしょうか?

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

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

takotakot, Y.H., papinianus👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

サブクラスから 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

1<?php 2 3class foo { 4 public function hoge() { echo 'hoge'; } 5 public static function fuga() { echo 'fuga'; } 6} 7 8class bar { 9 public function piyo() { 10 echo foo::hoge(); 11 } 12} 13$x = new bar(); 14$x->piyo();

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

php

1class bar { 2 public function piyo() { 3 echo foo::fuga(); 4 } 5}

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

php

1<?php 2 3class foo { 4 public const X = 1; 5 public static $y = 2; // staticがないとFatal error 6} 7 8class bar { 9 public function piyo() { 10 echo foo::X . PHP_EOL; 11 echo foo::$y . PHP_EOL; 12 } 13} 14$x = new bar(); 15$x->piyo();

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

投稿2018/12/11 10:41

編集2018/12/12 01:53
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

7968

2018/12/11 11:18

回答と参考サイトありがとうございます。 また、説明文も考えてくださり、ありがとうございます。 私の質問の仕方にも問題があったかもしれません((_ _ (´ω` ) まず、self:: という記述は self キーワードにスコープ定義演算子を付けてるという理解です。 parent :: も同じく parent キーワードにスコープ定義演算子を付けてるという理解です。 self は記述してる自クラスを、parent は親クラスを返すという理解です。 スコープ定義演算子は下記の3つにアクセスできるとPHPマニュアルに記載があります。 1. static 2. 定数 3. オーバーライドされたクラスのプロパティやメソッド 今回のコードの場合だと、B クラスの fuga メソッドの場合は親クラスにある static ありの getStaticName メソッドをコールしているため「 1. static」に該当しているかと思います。 これは納得できます。 ``` public function fuga($args1, $args2) { parent::getStaticName($args1, $args2); } ``` もう一つの B クラスにある hoge メソッドは 1〜 3 のどれにも該当しないと思うのですが、なぜ parent:: というスコープ定義演算子を使ってアクセスできるのでしょうか? お手すき時にアドバイスいただると助かります((_ _ (´ω` )
papinianus

2018/12/11 19:09

回答したはずの者が言うのは変なのですが > クラス(静的プロパティ、静的メソッド)にアクセスするのがダブルコロン。 とのことですが、「実際の挙動としてparent::でparentの動的メソッドにアクセス可能である」が質問の趣旨だと思います。また、検証してみた結果、m6u様の回答やマニュアルにそわない動作をしているように思えてきました。
退会済みユーザー

退会済みユーザー

2018/12/12 00:38

クラスの持ち物が静的プロパティ、静的メソッドで、 インスタンスの持ち物が動的プロパティ、動的メソッドという解釈。 スコープ演算子がアクセスできるのがクラスの持ち物である静的プロパティ、静的メソッド。 だから、 「オーバーライドされたクラスのプロパティやメソッド」 というよりも 「オーバーライドされたクラスの、public宣言protected宣言された、静的プロパティや静的メソッド」 が正しいところ。
退会済みユーザー

退会済みユーザー

2018/12/12 00:46

phpのマニュアルの記載は、特に日本語訳は、きれいに正しく解釈できるとは言えず、 部分を取り上げると誤解を生じる場合のありますので、 よその解説記事やサンプルコードを動かすなどして staticをつけたり外したり、publicをprotectedやprivateに書き換えてみるなどして、 いじめてみるとよく分かると思います。
7968

2018/12/12 01:00 編集

お二方、何度も返信していただきありがとうございます((_ _ (´ω` ) > クラスの持ち物が静的プロパティ、静的メソッドで、 > インスタンスの持ち物が動的プロパティ、動的メソッドという解釈。 私の中では static があるプロパティとメソッドが静的プロパティと静的メソッドという解釈です。 static がないプロパティとメソッドが動的プロパティ、動的メソッドという解釈です。 上記のような理解ですので static のない親クラスに partent:: でアクセスできるのが腑に落ちませんでした。 m6u さんは static のあり・なし関係なしに下記のように記述した定義は全て静的プロパティ、静的メソッドという解釈なのでしょうか? class A { public $a = 1; public static $b = 2; public function hoge() {} public static function fuga() {} } 動的プロパティや動的メソッドは、あくまでも new クラス名 でインスタンス化して使うプロパティやメソッドが動的プロパティ、動的メソッドという解釈なのでしょうか? お手すき時にアドバイスいただると助かります((_ _ (´ω` )
退会済みユーザー

退会済みユーザー

2018/12/12 01:52

スコープ演算子をクラスと継承の話だけですることに違和感を覚えたので、 回答内に説明を加えました。
退会済みユーザー

退会済みユーザー

2018/12/12 08:03

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

2018/12/13 02:35 編集

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

0

ベストアンサー

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

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


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

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

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

php

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

現在の私の理解としては

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

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

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

php

1class A { 2 public function a() { return 1; } 3} 4class B extends A { 5 public function b() { return parent::a(); } 6} 7class C extends A { 8 public function c() { return A::a(); } 9} 10class D extends A { 11 public function d() { return $this->a(); } 12} 13class E extends A { 14 public function e() { return self::a(); } 15} 16// class F { 17// public function f() { return A::a(); } //fatal error 18// } 19$b = new B(); 20$c = new C(); 21$d = new D(); 22$e = new E(); 23//$f = new F(); 24// A::a(); // fatal error 25var_dump($b->b()); 26var_dump($b->b1()); 27var_dump($c->c()); 28var_dump($d->d()); 29var_dump($e->e()); 30//var_dump($f->f());

投稿2018/12/11 09:34

編集2018/12/11 18:52
papinianus

総合スコア12705

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

7968

2018/12/11 11:35

回答ありがとうございます。 > staticと、static::は別物です。 プロパティやメソッドの前に static を付けると静的プロパティ、静的メソッドになるという理解です。 static:: は staic キーワードにスコープ定義演算子を付けているという理解です。 > static::もparent::もクラスを返します。 例えば、parent なら親クラスを指定しているという理解です。 $this->の代わりにself::としても動作します。 self:: は statc ありのプロパティにアクセスするものなので、プロパティの前に statc を付ければ $this-> を self:: に変えても動作するということですよね?
papinianus

2018/12/11 19:04

結論から申しまして質問者さまが言っておられるとおりparent::でインスタンスメソッドが呼べるのはマニュアルのうえではおかしい、と思うに至りました。回答を追記しました。 > static:: は static キーワードにスコープ定義演算子を付けているという理解です。 「static キーワードにスコープ定義演算子を付けている」というのは変ではないかと思います。引用なさっておられるとおり、マニュアルに従えば本来的に`::`はstaticなプロパティやメソッドへのアクセスしか許さないはずなので、それにさらにstaticをかけるというのは変です。static::におけるstaticはプロパティやメソッドにつける「静的」ということとは違う働きと考えたほうがよいと思います(m6u様の引用のphpを愛する試みが非常に参考になります) > self:: は statc ありのプロパティにアクセスするものなので、プロパティの前に statc を付ければ $this-> を self:: に変えても動作するということですよね? こちらは回答にも追記しましたが、parent::と同じく、self::によって、staticでない自クラスのメソッドにアクセス可能です。
7968

2018/12/12 01:44 編集

ご返信ありがとうございます。 > マニュアル自身、クラスの基礎の「例2 $this 疑似変数の例」において、A::foo();という書き方で、クラスAのstaticでもオーバーライドされてもいないメソッドfooにアクセスする例を書いてます。 そうなんですよね、 deprecated や strict の通知が表示されますが、クラスの外から :: で static のないメソッドにアクセスできるんですよね。 これは意図していない動作で今後はエラーになる可能性もあると下記の記事で強そうな方がそのようなことを言及されていたと思います。(今はqrunch がエラーで見れない;;) https://qrunch.io/entries/nXidDatSJfnn9OnS/NiCLd1tcAqFPKHNs > プロパティについて言えば逆にオーバーライドしていてもstaticでないとparent::によって参照することができませんでした。 static なしの親プロパティにアクセスできるか試してはいなかったのですが、仰るとおり、Fatal error になりました。 サンプルコードありがとうございます。 > 先に書いたself::についてはこれもstaticでない自クラスのメソッドを($this->でなくself::で)呼べるという意図でした。下記のパターンもご参考としていただければと思います。 なんと、self:: も static ではない自クラスのメソッドを呼べるのですね。うーむ。 PHP のマニュアルのスコープ定義演算子の説明が正しくない、もしくは、 ・static, ・定数 ・オーバーライドされたクラスのプロパティやメソッド はクラスの外からアクセスするときの話で、クラスコンテキスト内で使う :: は別という解釈をすれば辻褄があうのかなぁと思いました。
papinianus

2018/12/12 02:15

> クラスの外からアクセスするときの話 これがm6u様が追記なさった名前空間の話だと思いますが、それでもマニュアルの、static, 定数 およびオーバーライドされたクラスのプロパティやメソッド、という書き方は不適切だと思います。英独仏版を比較参照しても、7968様が仰るとおり ・static ・定数 ・オーバーライドされたクラスのプロパティやメソッド の3つに読み取るのが妥当です。しかし、クラス外からClass::method()とするときは、オーバーライドされたクラスの静的(static)メソッドに限られるため、最低でもマニュアルの表現はstaticが3つ目を修飾する形容詞となるようにかえなければ不正確だと思います。
7968

2018/12/12 08:01 編集

何度もご返信いただき、ありがとうございます。 私の考えと papinianus さんの考えは、ほぼ同じなのかなと思っています。 PHP マニュアルを English にして確認しても「オーバーライドされたクラスのプロパティやメソッド」というのは誤訳ではないと思っています。 ちなみにですが「オーバーライド」という言葉の意味について確認させてください。 「オーバーライドされたクラスのプロパティやメソッド」というのは普通は下記のように static なしのプロパティやメソッドをサブクラスで再定義することをオーバーライドと呼びますよね? ``` <?php class A { public $hoge = 'Aプロパティ'; public function fuga() { return 'Aメソッド'; } } class B extends A { public $hoge = 'Bプロパティ'; public function fuga() { return 'Bメソッド'; } } // echo B::$hoge; これはFatal error echo B::fuga(); // Strict Standards の警告が表示されるけど、結果も表示される ``` 下記のように static ありのプロパティやメソッドをサブクラスで再定義することはオーバーライドとは呼びませんよね? ``` <?php class A { public static $hoge = 'Aプロパティ'; public static function fuga() { return 'Aメソッド'; } } class B extends A { public static $hoge = 'Bプロパティ'; public static function fuga() { return 'Bメソッド'; } } echo B::$hoge; // Bプロパティ echo B::fuga(); // Bメソッド ``` これを仮にオーバーライドと呼んだとしても、`A::$hoge` や `A::fuga()` でアクセスできるので、オーバーライド関係なしにアクセスできるので「オーバーライドされた」と記述する意味はないのかと思っています。 「オーバーライドされたクラスのプロパティやメソッド」というのは何を言いたかったのでしょうかね?(あれ?作者の気持ちを答えよという国語の問題?w) 結果から推察するにしても static なしのプロパティは fatal error で、メソッドはアクセスできても警告が表示されますからね。 「オーバーライドされたクラスのプロパティやメソッド」のように「プロパティとメソッド」とは記載しないですよね... static を付ければオーバーライド関係なしにアクセスできるので、「オーバーライドされた」とは記載しないですよね... うーん...
7968

2018/12/12 08:09

長々と書いてしまいましたが、PHPマニュアルの説明は誤りで、papinianus さんが記載された下記の内容が正しいということですね。 「ダブルコロン」は、トークンのひとつです。 「定数」または「staticなクラスのプロパティ」、「クラスのメソッド」にアクセスすることができます。
papinianus

2018/12/12 08:13

私も分かったつもりでしたが今や7869様と同じ疑問を持っています。 静的でもオーバーライドと呼ぶと思います(もちろん、偶然同じじゃなくて、継承関係がある場合) ・「オーバーライドされた」というのの読解 オーバーライドされた(親クラス)側は、インスタンスが子クラスであれば、どのようにメソッドをコールしても(PHPでは難しいですが親のクラス型の変数に代入してメソッドを呼んでも)、オブジェクト指向の多態性によって、親(オーバーライドされた)側のメソッドは呼ばれない、となるのがオブジェクト指向的な理解だと思います。 それを`::`の前にクラスを指定したら、呼び出し元のオブジェクトコンテクスト(インスタンス)によって$thisが何になるかにかかわらず、指定されたクラスのメソッドが使える、ということに意味がる文章だと理解しています。この説明もなんか回りくどいですが。 くだけて言えば、「->だと隠されちゃうメソッドに::だったらアクセスできるよ」という意図かと思ってます(、今のところは)。
papinianus

2018/12/12 08:13

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

2018/12/13 02:35 編集

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

2018/12/13 04:24

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問