6

いくつかのPHPクラスをデバッグしようとしているときに、私はいくつかの動作に遭遇しました。これは、私の考えでは、本当に奇妙なことです。

以下の動作のデモンストレーションを作成しました。

class BaseClass {
   public function baseMethod () {
      echo (implode (' ', $this -> childMethod ()) . PHP_EOL);
   }
}

class ChildClass extends BaseClass {
   protected function childMethod () {
      return array ('What', 'The', 'Actual', 'Fork!');
   }
}

$a = new ChildClass ();
$a -> baseMethod ();

さて、私の考えでは、基本クラスは、抽象メソッドを宣言(または継承)するか、インターフェースを実装することによってサブクラスに強制するものを除いて、サブクラスについてまったく仮定を立てることができないはずです。ただし、上記のコードは実際には文字列を出力し、エラーをスローしません。

なんと実際のフォーク!

これは私には壊れた振る舞いのようです。基本クラスが宣言しない限り、abstract protected function childMethod();それを呼び出すことはできませんね。

私はインターネットを精査して、これが予想される動作であることを示す何かを見つけようとしています。これまでのところ、私が見つけたのは、PHPのマニュアルから次のとおりです。

他のオブジェクトからの可視性

同じタイプのオブジェクトは、同じインスタンスでなくても、互いにプライベートメンバーと保護されたメンバーにアクセスできます。これは、これらのオブジェクト内で実装固有の詳細がすでにわかっているためです。

それで、私がここで目撃している振る舞いは正しいですか、それともこれはPHPのバグですか?それは私には間違っているように見えるので、確かに私が頼りにする行動ではありません。

参考までに、実際のコードで見つかった問題は、サブクラスがスーパークラスが呼び出そうとしているプラ​​イベートメソッドを宣言したことでした。スーパークラスはメソッドabstractを宣言しませんでした(宣言した場合は、少なくとも保護する必要がありました)。

4

3 に答える 3

8

さて、私の考えでは、基本クラスは、抽象メソッドを宣言(または継承)するか、インターフェースを実装することによってサブクラスに強制するものを除いて、サブクラスについてまったく仮定を立てることができないはずです。

これが、静的に型付けされた言語での動作方法です。PHP(および他の多くの)では、任意の値で任意のメソッドを自由に呼び出すことができます(オブジェクトである必要はありません)。呼び出しに意味がない場合は、ランタイムエラーが発生します。

もちろん、派生クラスでメソッドを実装することが期待される場合は、メソッドを宣言することをお勧めします。これは単に良い習慣です。さらに、多くの有名なPHP IDEは、このような欠落を検出し、呼び出しにフラグを立てて、コンパイラーができないために注意を喚起します。

これは私には壊れた振る舞いのようです。基本クラスが宣言しない限り、 abstract protected function childMethod();それを呼び出すことはできませんね。

上で説明したように、それはすべきです。この動作が望ましくない場合は、PHPを使用する必要のある言語ではありません。

protected余談ですが、密接に関連するメモでは、基本クラスが派生クラスで定義されたメンバーに問題なくアクセスできることも直感に反する場合があります。これは文書化されています。実際、それはまさにあなたの例で起こっていることですが、それは別の種類の直感に反するものであるため、具体的に言及します(あなたの例は、メソッドがあった場合にも不可解になりますpublic)。

PHPが許可する、静的に型付けされた観点からも「不可能であるべき」構造の無限のリストを検討してください。これには、次のものが含まれます。

// #1
$varName = 'foo';

// How do you know $object has a property named "foo"?
// How do you know that "foo" is a valid property name in the first place?
// How do you know that $object is an object to begin with?
echo $object->$varName;

// #2
$object = new SomeObject;
$methodName = 'someMethod';

// it's practically impossible to reason about this before runtime
call_user_func(array($object, $methodName));
于 2013-01-29T13:22:25.003 に答える
2

バグではありません。そのように動作するはずです。メソッドの呼び出しについてコメントしてみましょう。

class BaseClass {
   public function baseMethod () {
      echo (implode (' ', $this -> childMethod ()) . PHP_EOL);
   }
}

class ChildClass extends BaseClass {
   protected function childMethod () {
      return array ('What', 'The', 'Actual', 'Fork!');
   }
}

/****/

$a = new ChildClass ();
/** $a is now instance of ChildClass, that is a sublass of BaseClass
 *  so the Object $a has 2 methods: baseMethod() inherited from BaseClass and childMethod() from ChildClass.
 */

$a -> baseMethod ();
/** now you're calling $a->baseMethod(), which will do a method call on $this->childMethod(). 
 *  As $this is refering $a here, $a has indeed a method named childMethod(), that will be called.
 */

あなたが疑問に思っているのは、での可視性protectedです。ただし$a、ChildClassのタイプと同様に、保護されたメソッドは、そのメソッドと継承されたメソッドに対して確実に表示されbaseMethod()ます。これは、すべてのOOP言語で同じである必要があります。

コードの悪い部分は、baseMethod()を呼び出すことの内部の仮定childMethod()です。そのような方法がない場合、それは常にエラーで終わります。JavaのようなOOP言語はここでコンパイルエラーをスローしますが、PHPには事前実行コンパイラがないため、PHPはスローしません。初期化をeg$a = new ChildClass ();から$a = new BaseClass ();に変更すると、PHPでもランタイムエラーが発生します。

于 2013-01-29T13:08:40.710 に答える
0

PHPは非常に動的な言語であり、継承なしでポリモーフィズムを使用できます。この場合、実行時にメソッド'childMethod'を検索するだけで、存在するため、メソッドを呼び出します。どうやってその方法を手に入れたのかは重要ではありません。__callmagicメソッドを使用してそのメソッドを動的に生成することもできます。これをC++やJavaで行うことはできませんが、PHPやJavascriptなどの言語ではすべて問題なく、より多くのオプションが提供されます。

于 2013-01-29T13:15:57.563 に答える