web-dev-qa-db-ja.com

PHP:親クラスから子クラスの関数を呼び出す方法

親クラスから子クラスの関数を呼び出すにはどうすればよいですか?このことを考慮:

class whale
{
  function __construct()
  {
    // some code here
  }

  function myfunc()
  {
  // how do i call the "test" function of fish class here??
  }
}

class fish extends whale
{
  function __construct()
  {
    parent::construct();
  }

  function test()
  {
    echo "So you managed to call me !!";
  }

}
56
Sarfraz

それが abstract classes の目的です。抽象クラスは基本的に次のように言います:私から継承している人は誰でも、この関数(またはこれらの関数)を持たなければなりません。

abstract class whale
{

  function __construct()
  {
    // some code here
  }

  function myfunc()
  {
    $this->test();
  }

  abstract function test();
}


class fish extends whale
{
  function __construct()
  {
    parent::__construct();
  }

  function test()
  {
    echo "So you managed to call me !!";
  }

}


$fish = new fish();
$fish->test();
$fish->myfunc();
98
FlorianH

さて、この答えは非常に遅いですが、なぜ誰もこれを考えなかったのですか?

Class A{
    function call_child_method(){
        if(method_exists($this, 'child_method')){
            $this->child_method();
        }
    }
}

そして、メソッドは拡張クラスで定義されています:

Class B extends A{
    function child_method(){
        echo 'I am the child method!';
    }
}

したがって、次のコードでは:

$test = new B();
$test->call_child_method();

出力は次のようになります。

I am a child method!

私はこれを使用して、can子クラスによって定義されるがそうである必要のないフックメソッドを呼び出します。

27
Christian Engel

技術的には、クジラのインスタンス(親)から魚のインスタンス(子)を呼び出すことはできませんが、継承を扱っているため、とにかく魚のインスタンスでmyFunc()を使用できるため、$yourFishInstance->myFunc()を呼び出すことができます直接。

テンプレートメソッドパターン を参照している場合は、メソッド本体として$this->test()と記述するだけです。 fishインスタンスからmyFunc()を呼び出すと、fishインスタンスのtest()への呼び出しが委任されます。ただし、クジラインスタンスから魚インスタンスへの呼び出しはありません。

補足として、クジラは哺乳類ではなく、魚です;)

15
Gordon

さて、この質問には多くの間違いがあります。どこから始めればいいのかわかりません。

第一に、魚はクジラではなく、クジラは魚ではありません。クジラは哺乳類です。

第二に、親クラスに存在しない親クラスから子クラスの関数を呼び出したい場合、抽象化に重大な欠陥があるため、ゼロから再考する必要があります。

第三に、PHPでできること:

function myfunc() {
  $this->test();
}

whaleのインスタンスでは、エラーが発生します。 fishのインスタンスで動作するはずです。

10
cletus

PHP 5.3以降では、staticキーワードを使用して、呼び出されたクラスからメソッドを呼び出すことができます。つまり:

<?php
class A {
    public static function who() {
        echo __CLASS__;
    }
    public static function test() {
        static::who(); // Here comes Late Static Bindings
    }
}

class B extends A {
    public static function who() {
        echo __CLASS__;
    }
}

B::test();
?>

上記の例は次を出力します:B

ソース: PHP.net/Late Static Bindings

6
Agustin

抽象クラスに行きます。
しかしPHPしないする必要があります動作させるためにそれらを使用します。親クラスのコンストラクターの呼び出しでも「通常"メソッド呼び出しとオブジェクトは、この時点で完全に「操作可能」です。つまり、$ thisは、継承されているかどうかにかかわらず、すべてのメンバーを「認識」しています。

class Foo
{
  public function __construct() {
    echo "Foo::__construct()\n";
    $this->init();
  }
}

class Bar extends Foo
{
  public function __construct() {
    echo "Bar::__construct()\n";
    parent::__construct();
  }

  public function init() {
    echo "Bar::init()\n";
  }
}

$b = new Bar;

プリント

Bar::__construct()
Foo::__construct()
Bar::init()

つまり、class Foofunction init()について何も知らないにもかかわらず、ルックアップは$ thisが参照するものに基づいているため、メソッドを呼び出すことができます。
それが技術面です。しかし、実際にメソッドを抽象化する(子孫に実装を強制する)か、上書き可能なデフォルトの実装を提供することにより、そのメソッドの実装を強制する必要があります。

3
VolkerK

私はこれがおそらくあなたにとって少し遅いことを知っていますが、私もこの問題を回避しなければなりませんでした。他の人がなぜこれが時々必要なのか理解するのを助けるために、ここに私の例があります:

アプリケーションのMVCフレームワークを構築しています。ベースコントローラークラスがあり、個々のコントローラークラスによって拡張されています。各コントローラーには、コントローラーが何をする必要があるかに応じて、異なる方法があります。たとえば、mysite.com/eventはイベントコントローラーを読み込みます。 mysite.com/event/createは、イベントコントローラーをロードし、 'create'メソッドを呼び出します。 create関数の呼び出しを標準化するには、コントローラーごとに異なる子クラスのメソッドにアクセスするためのベースコントローラークラスが必要です。コードに関しては、親クラスがあります。

class controller {

    protected $aRequestBits;

    public function __construct($urlSegments) {
        array_shift($urlSegments);
        $this->urlSegments = $urlSegments;      
    }

    public function RunAction($child) {
        $FunctionToRun = $this->urlSegments[0];
        if(method_exists($child,$FunctionToRun)) {
            $child->$FunctionToRun();
        }
    }   
}

次に、子クラス:

class wordcontroller extends controller {

    public function add() {
        echo "Inside Add";
    }

    public function edit() {
        echo "Inside Edit";
    }

    public function delete() {
        echo "Inside Delete";
    }
}

したがって、私の場合の解決策は、子インスタンス自体をパラメーターとして親クラスに戻すことでした。

2
rca86

これが古い質問であっても、これはReflectionMethodを使用した私のソリューションです。

class whale
{
  function __construct()
  {
    // some code here
  }

  function myfunc()
  {
    //Get the class name
    $name = get_called_class();

    //Create a ReflectionMethod using the class and method name
    $reflection = new \ReflectionMethod($class, 'test');

    //Call the method
    $reflection->invoke($this);
  }
}

ReflectionMethodクラスを使用する利点は、引数の配列を渡し、呼び出しているメソッドで必要な引数を確認できることです。

    //Pass a list of arguments as an associative array
    function myfunc($arguments){
      //Get the class name
      $name = get_called_class();

      //Create a ReflectionMethod using the class and method name
      $reflection = new \ReflectionMethod($class, 'test');

      //Get a list of parameters
      $parameters = $reflection->getParameters()

      //Prepare argument list
      $list = array();
      foreach($parameters as $param){

          //Get the argument name
          $name = $param->getName();
          if(!array_key_exists($name, $arguments) && !$param->isOptional())
            throw new \BadMethodCallException(sprintf('Missing parameter %s in method %s::%s!', $name, $class, $method));

          //Set parameter
          $list[$name] = $arguments[$name];
        }

      //Call the method
      $reflection->invokeArgs($this, $list);
    }
1
gpmaccari

これを行う唯一の方法は、 reflection を使用することです。ただし、反射は高価であり、必要な場合にのみ使用してください。

ここでの本当の問題は、親クラスが子クラスメソッドの存在に依存してはならないことです。これはOODの基本原則であり、設計に重大な欠陥があることを示しています。

親クラスが特定の子に依存している場合、それを拡張する可能性のある他の子クラスでは使用できません。親子関係は抽象化から特異性へと進み、逆ではありません。代わりに、必要な関数を親クラスに配置し、必要に応じて子クラスでオーバーライドする方がはるかに良いでしょう。このようなもの:

class whale
{
  function myfunc()
  {
      echo "I am a ".get_class($this);
  }
}

class fish extends whale
{
  function myfunc()
  {
     echo "I am always a fish.";
  }
}
1
zombat

子クラスにメソッドが存在する場合、メソッドは親クラスから呼び出されます(存在する場合はオプションのコールバックとして)

<?php

    class controller
    {

        public function saveChanges($data)
        {
            //save changes code
            // Insert, update ... after ... check if exists callback
            if (method_exists($this, 'saveChangesCallback')) {
                $arguments = array('data' => $data);
                call_user_func_array(array($this, 'saveChangesCallback'), $arguments);
            }
        }
    }

    class mycontroller extends controller
    {

        public function setData($data)
        {
            // Call parent::saveChanges
            $this->saveChanges($data);
        }

        public function saveChangesCallback($data)
        {
            //after parent::saveChanges call, this function will be called if exists on this child
            // This will show data and all methods called by chronological order:
            var_dump($data);
            echo "<br><br><b>Steps:</b><pre>";
            print_r(array_reverse(debug_backtrace()));
            echo "</pre>";
        }
    }

$mycontroller = new mycontroller();
$mycontroller->setData(array('code' => 1, 'description' => 'Example'));
0
XPerez

とても簡単です。これは抽象クラスなしで実行できます。

class whale
{
  function __construct()
  {
    // some code here
  }

  /*
  Child overridden this function, so child function will get called by parent. 
  I'm using this kind of techniques and working perfectly.  
  */
  function test(){
     return "";
  }

  function myfunc()
  {
    $this->test();
  }
}

class fish extends whale
{
  function __construct()
  {
    parent::construct();
  }

  function test()
  {
    echo "So you managed to call me !!";
  }

}
0
rishad2m8