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

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

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

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

Q&A

解決済

3回答

1085閲覧

php.netの遅延静的束縛サンプルコードの動きについて

moriman

総合スコア615

PHP

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

0グッド

0クリップ

投稿2019/02/04 06:40

先日質問したサンプルコードと同じで恐縮なのですが、改めて考えなおしてみるとまたこんがらがってしまいました。(厳密に言うと前回の質問とは別の部分についてなのですが。)
php.netの遅延静的束縛のページの、例3 非静的コンテキストにおける static:: の使用法

php

1<?php 2class A { 3 private function foo() { 4 echo "success!\n"; 5 } 6 public function test() { 7 $this->foo(); 8 static::foo(); 9 } 10} 11 12class B extends A { 13 /* foo() が B にコピーされるので、メソッドのスコープは A のままとなり、 14 * コールは成功します */ 15} 16 17class C extends A { 18 private function foo() { 19 /* もとのメソッドが置き換えられるので、新しいメソッドのスコープは C となります */ 20 } 21} 22 23$b = new B(); 24$b->test(); 25$c = new C(); 26$c->test(); //fails 27?>

result

1success! //➊ 2success! //➋ 3success! //➌ 4 5 6Fatal error: Call to private method C::foo() from context 'A' in /tmp/test.php on line 9 7//➍

について質問して、➌以外の結果については理解できたのですが、➌の結果、つまり
$c->test();

$this->foo();
の部分がなぜsuccess!と表示されるのかわかりません。

前回頂いた解答で、➌の部分では$thisはクラスAを指す、だからクラスAのfoo()が実行されてsuccess!と表示される、とあったのですが、改めて考えてみると、$thisは$cを指すように思います。
クラスCではfooメソッドがオーバーライドされているので、$cのfoo()が呼ばれると、Cクラスのfooメソッド、つまり空のメソッドfoo()が実行される、つまり➌は「何も表示されない」という動作になるのではないか、と思うのですが、実際動かすとsuccess!と表示されます。

$thisが$cを指すと思う理由↓

php

1class PPP{ 2 public function pri(){ 3 echo 'pppPPPP'; 4 } 5 public function meth(){ 6 echo $this->pri(); 7 } 8 9} 10 11 12class QQQQ extends PPP{ 13 public function pri(){ 14 echo 'qqqqq'; 15 } 16} 17 18$ins_Q=new QQQQ; 19 20$ins_Q->meth(); 21//結果: qqqqq

まず$thisは$cを指す、ということでよいでしょうか?
あとなぜこの結果になるのでしょうか?
よろしくお願い致します。

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

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

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

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

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

guest

回答3

0

papinianus様、私の回答へのフォローありがとうございます。

そしてmoriman様、すみません、前回のサンプルコード中のコメントで「privateメソッド独自……というか、アクセス権が無い場合のふるまいである」ということを明示しなかったため、混乱を招いてしまったものと思われます。

もし、fooメソッドがpublicメソッドだったとすると、AクラスのtestメソッドからAクラスのfooメソッドを呼ぼうとする……が、「AクラスのfooメソッドはCクラスのfooメソッドにオーバーライドされてしまっている(「Aクラスのfooメソッドだったもの」は上書きされてしまったため存在しない)」ので、結局、Cクラスのfooメソッドが呼ばれる、という挙動になります。

privateメソッドとpublicメソッドのオーバーライド時の挙動の違いについては、下記の記事が参考になるかと思います(2ページめに、まさに「privateメソッドだとオーバーライドされない」の件が書かれています)。

http://www.atmarkit.co.jp/ait/articles/1508/05/news021.html

投稿2019/02/04 10:55

nak

総合スコア696

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

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

moriman

2019/02/07 17:27

nak様、解答を頂きさらに参考記事を提示して頂きまして本当にありがとうございます。 atmarkitの記事中の説明については、 継承した場合は名前が同一であっても「クラスごとに別々の値を持つ」ことになり、「上書きされず、それぞれ別に値を持っている」ような形になります。 とあり、この記事ではクラスのメンバプロパティの上書きについて書いてあります。またその少し後に 「プロパティとメソッドについては特に違いはないので、」 という文言もあります。しかし記事のそれより後ろの部分については、疑問点である「親クラスのprivateメソッドと同名のメソッドを子クラスで定義した時オーバーライドできないので・・・」という話とは少し違う話なのかなと思いました。 いろいろネットで調べた結果、上記のphp.netのサンプルコードの➌で言うと、クラスA(親クラス)のfooメソッドがprivateであれば、クラスC(子クラス)で親クラスと同名のfooメソッドを定義してもそれはオーバーライドではない。クラスCのfooメソッドがprivateでもpublicでも同じで、オーバーライドではない。結果としてはAクラスのfooメソッドが呼ばれる、ということのようです。(実際プログラムを動かしてもそうなる。でもなぜそうなるかはよくわからない。) 特に子クラスのfooメソッドがpublicの時も同じ結果、というのが違和感を感じます。 $thisは$cを指す、さらに子クラスのfooメソッドはpublicなので、Cクラスのfooメソッドが呼ばれそうな気がします。でも実際の結果は違う。 何か認識が間違っている箇所がありますでしょうか?
nak

2019/02/08 04:54

> 特に子クラスのfooメソッドがpublicの時も同じ結果、というのが違和感を感じます。 そうですね。 私も改めて動作検証をしてみて、だんだんわけがわからなくなってきました……。 > $thisは$cを指す、さらに子クラスのfooメソッドはpublicなので、Cクラスのfooメソッドが呼ばれそうな気がします。でも実際の結果は違う。 実は、上記パターンについては、恥ずかしながら「アクセス識別子が異なる状態でのメソッド上書き」をしようと思ったことがなかったので、認識が漏れておりました(今回「そんなことができるんだ……」と驚いている次第です。勉強になりました)。 そして、このパターンの挙動を考えると、「publicで宣言されているメソッドの場合、親クラスの同名メソッドにアクセスできなくなる(ただし、"parent::"キーワード等を使って、明示的にアクセスしようとした場合を除く)」とは言えなくなってしまいますね……うーん。 privateと$thisについては下記の記事で詳しく説明されているようです。 もしかしたら既にご覧になっていらっしゃるかもしれませんが、よろしければ参考にしてみてください(とはいえ、結局「どうしてこういう挙動なのか?」は、よくわかりませんが……)。 https://1design.jp/web-development/1898 すっきりしない回答で申し訳ありません。
moriman

2019/02/09 02:58

いえいえとんでもないです。私の疑問にお付き合いいただきしてありがとうございました。 私としても実用度がどれくらいあるのかわからず、php.netにあったので考えていました。 とりあえず現時点でこの件に時間を使うのはこれくらいにしておこうかと思います。
guest

0

完全に他人の褌なんですが。

前回頂いた解答で、➌の部分では$thisはクラスAを指す、だからクラスAのfoo()が実行されてsuccess!と表示される、とあった

nak様回答のベストアンサーベースで申しますが、そのようなことは書かれておられません。

この場合、「そのオブジェクト」は $c です。

また、$c は new C() を代入されているので「そのオブジェクトのクラス」は C ということになります。
その上で……。
test メソッドは Aクラスにしか存在していません。
そのため、 $c->test(); を実行した場合、呼び出されるのは A クラスのメソッドです。

この1行目で、$thisclass Cであるという質問者様ご認識のとおりのことが書かれています。
"その上で"、testはAにしかないだから呼び出されるのは A クラスのメソッドとなっています。

このAから見たときに、どのprivateが参照できるか、という話

投稿2019/02/04 07:16

編集2019/02/04 07:37
papinianus

総合スコア12705

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

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

0

ベストアンサー

$thisが$cを指すと思う理由↓

こちらはpublicになっているのに対して、上の例ではメソッドがprivateです。privateであるメソッドは、継承先からも見えないためオーバーロードされず、A::fooがそのまま見えています。。

投稿2019/02/04 06:51

maisumakun

総合スコア145183

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

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

moriman

2019/02/07 16:47

解答を頂きましてありがとうございます。 確かに上の例では親クラス内のオーバーライドされるメソッドfooがprivateです。 このサンプルに関連したネット記事をいくつか探したのですが、やはりどの記事も「オーバーライドされない」という説明があります。結果としては親クラスであるAクラスのfooメソッドが呼ばれ➌の結果になる、というものです。 上の例の➌では子クラスであるCクラス内のオーバーライドするfooメソッドはprivateですが、仮にこの子クラスC内のfooメソッドがpublicであっても、やはりオーバーライドされず、結果は(子クラスC内のfooメソッドが)privateの時と同じだと思うのですが、これがどうもすっきりしません。 $thisはインスタンス$cを指す、さらに、オーバーライドされないとはいえCクラス内でpublicのfooメソッドを定義しているのでCクラスのfooメソッドが呼び出されてもおかしくない気がするのですが、 今までの考え方でどこか認識がおかしい部分がありますでしょうか?
maisumakun

2019/02/08 00:50

自分としては、「これで一貫した設計になっている」と判断します。 privateメソッドは継承先からも見えない、つまり基底クラスに隠蔽されているものです。static::のように意図的に参照しようとしたのならともかく、privateメソッドを上書きできてしまっては、基底クラスの動作を派生クラスで破壊することも可能となってしまいます。 「privateメソッドは基底クラスに固有な実装の詳細、継承で上書き可能にしたければprotectedなどを使うべき」というところかと思います。
moriman

2019/02/09 03:00

わかりやすい解説をありがとうございました。 そのように理解しておこうと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問