将函数传递给类

时间:2009-11-14 07:21:48

标签: php class

PHP mysql数据库 我在这里创建了一个跟随问题specifically about pagination

我需要从另一个类中调用一个方法,并且能够更改被调用的方法。像这样

class db{

    function a(){ echo 'I run a query';}
    function b(){ echo 'im the other query';}

}


class YourClass {

    var $fcn;
   $db = new db()

    function invoke(){
         call_user_func($this->fcn);
    }

} 



$instance = new YourClass;
$instance->fcn = 'db->a';
$instance->invoke();

我想在'yourClass'方法'invoke'中使用db类中的方法'a' 感谢

好的,这就是我从所提供的答案中整理出来并且有效。

    class A {

    function a(){ 
        $x = 'Method a is used';
        return $x;
        } 
    function b(){ 
        $x = 'Method b is used';
        return $x;
        } 
}

class B {

    function invoke($obj, $method){

        echo call_user_func( array( $obj, $method) );

    }

} 

$instance = new B();
$instance->invoke(new A(),"a");

其中写道,“方法a被用于”屏幕

但我真的希望能够将参数传递给方法“a”,所以我尝试了下面的代码。

class A {

    function a($var1,$var2,$var3){ 
        $x = 'the three passed values are  ' . $var1 . ' and ' . $var2 . ' and ' . $var3;
        return $x;
        } 
    function b(){ 
        $x = 'im method b';
        return $x;
        } 
}

class B {

    function invoke($obj,$arguments){

        echo call_user_func_array($obj,$arguments);

    }

} 

$arguments = array('apple','banana','pineapple');
$use_function = array(new A(),"a");

$instance = new B();
$instance->invoke($use_function,$arguments);

它几乎可以正常工作,但我将这些错误置于正确的答案之上

A :: a()缺少参数1,参数2和3缺少参数,但答案打印到屏幕 “三个传递的价值观是苹果,香蕉和菠萝”

我可能犯了一个新手的错误,我整天都在编码。如果有人可以修复上面的脚本并提交工作代码,我将永远感激不尽。我必须把这个问题放到床上才能上床睡觉。 感谢

9 个答案:

答案 0 :(得分:8)

从PHP5.3开始,您可以使用闭包或仿函数来传递方法。在此之前,你可以用create_function()编写一个匿名函数,但这很尴尬。

基本上,您尝试做的事情可以使用Strategy Pattern完成。

删除了示例代码,因为在OP更改问题后它不再有用(请参阅wiki)

除此之外,你可能想看看福勒斯的Data Source Architectural PatternsZend Framework(以及几乎所有其他PHP框架)提供了database access classes您可以用于这些模式,还有一个paginator class,所以为什么不检查它们以了解它们是如何做到的

删除了编辑1,因为在OP改变了问题后它不再有用(参见wiki)

编辑2 好的,让我们采取一步一步的方法(尽管不使用战略模式)

使用此代码可以轻松解决您在问题中要求的内容:

class Foo
{
    public function bar()
    {
        echo 'bar method in Foo';
    }
}

class MyInvoker
{
    protected $myObject;

    public function __construct()
    {
        $this->myObject = new Foo();
    }

    public function __call($method, $args)
    {
        $invocation = array($this->myObject, $method);
        return call_user_func_array($invocation, $args);
    }
}

使用此代码,您只需调用适当的方法即可。没有设置方法名称。没有笨拙的额外调用方法。不重新调用方法的方式。你不需要它,因为PHP有​​__call函数,你刚刚教导将MyInvoker中不存在的所有方法发送到$ myObject,例如FOO:

$invoker = new MyInvoker;
$invoker->bar(); // outputs 'bar method in Foo called'

您也可以将MyInvoker扩展为Foo的子类,例如

class MyInvoker extends Foo {}

然后你也可以这样做。这不是你需要的东西,它说明了做这样的事情是多么无意义。 MyInvoker现在无所作为。它是一个空类,实际上与Foo相同。即使使用前面使用__call方法的方法,它也没有做任何事情。这就是为什么我要求你更具体地说明期望的结果,这是一个Paginator。

首先尝试:

class Paginator()
{
    // A class holding all possible queries of our application
    protected $queries;

    // A class providing access to the database, like PDO_MySql
    protected $dbConn;

    public function __construct()
    {
        $this->db      = new MyPdo();
        $this->queries = new DbQueries();
    }

    public function __call($method, $args)
    {
        $invocation = array($this->queries, $method);
        $query      = call_user_func_array($invocation, $args);
        return $this->dbConn->query($query);
    }
}

使用该代码,我们的Paginator在其自身内部创建所需的一切,紧密耦合数据库连接类和所有查询,并允许您调用这些,如此

$paginator = new Paginator;
// assuming we have something like getImageCount() in DbQueries
echo $paginator->getImageCount();

当时发生的事情是,Paginator会认识到它不知道getImageCount()并会调用__call方法。 __call方法将尝试在DbQueries上调用getImageCount()方法。由于它存在,它将返回查询,而查询又传递给db连接以执行它。太棒了你会说,但事实并非如此。事实上,这太可怕了。您的分页器的责任是计算表中的项目并从该表中获取特定范围和数量的项目。但是现在,它没有做这样的事情。它完全没有告诉我们什么,所以让我们尝试一个新的课程:

class Paginator
{
    protected $dbConn;
    protected $itemCount;

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

    public function countItems($query)
    {
        $this->itemCount = $this->dbConn->query('select count(*) from (?)', $query);
        return $this->itemCount;
    }

    public function fetchItems($query, $offset = 0, $limit = 20)
    {
          $sql = sprintf('select * from (?) LIMIT %d, %d', $offset, $limit);
          return $this->dbConn->query($sql, $query);
    }
}

好多了。现在我们的Paginator是一个聚合而不是复合,这意味着它不会在其自身内实例化对象,而是要求它们在构造函数中传递给它。这称为dependency injection(并且当dbConn使用loose coupling时也提供interface),这将使您的应用程序更易于维护,因为现在可以轻松交换组件。当代码Unit Testing时,这也会派上用场。

此外,您的Paginator现在专注于它应该做的事情:计算和获取任意查询的项目。无需传递方法。不需要模糊的方法调用。你会这样使用它:

$paginator = new Paginator($dbConn);
$query     = $dbQueries->findImagesUploadedLastWeek(); // returns SQL query string
$images    = $paginator->countItems($query);
if($images > 0) {
    $images = $paginator->fetchItems($query);
}

就是这样。好吧,差不多。当然,你必须渲染分页。但如果你扩展你已经拥有的东西,这应该是相当微不足道的。 $ imageCount属性提示下一步该去哪里。

无论如何,希望我能说清楚。

P.S。 $this->dbConn->query($sql, $query)来电当然是虚拟代码。不要期望能够复制和粘贴它并让它工作。此外,您应确保添加到Paginator SQL的查询可以安全使用。您不希望有人插入删除所有数据库行的查询。永远不要相信用户输入。

P.P.S。 $query应该是SQL查询字符串。查看PDO::prepare的PHP手册。通常,它在执行语句之前准备语句会产生更好的性能和安全性。手册中的页面将为您提供有关查询调用中?的线索。如果您不想使用PDO,只需使用sprintf()str_replace()?替换为$query,例如$this->dbConn->query(sprintf('SELECT count(*) from (%s)', $query)但要记住,这没有准备好的声明的好处,并可能为SQL Injection漏洞打开大门。

P.P.P.S 是的,Dependency Injection通常是首选策略。这虽然是一个很有用的话题,但现在可能还不能完全掌握,但值得深入研究。现在,如果你试图赞成聚合而不是合成,那就足够了。你的类应该只做他们负责的事情,并通过构造函数获得任何依赖。

答案 1 :(得分:2)

以下是两种方法:

class YourClass {
    var $fcn;
    function invoke($arguments){
       //one way:
       $this->{$this->fcn}($arguments);
       //another way:
       call_user_func_array(array($this, $this->fcn), $arguments);
    }
    function a(){ 
       echo 'I am a()';
    }
} 

$instance = new YourClass;
$instance->fcn = 'a';
$instance->invoke();

这将打印出“我是一个()”来自课堂内。

答案 2 :(得分:1)

你快到了

    class db {
        function a(){ echo 'I run a query';}
        function b(){ echo 'im the other query';}
    }


    class YourClass {
        var $fcn;
        function __construct() {
             $this->db = new db();
        }
        function invoke() {
             call_user_func(array(
                $this->{$this->fcn[0]}, 
                $this->fcn[1]
            ));
        }

    }

    $instance = new YourClass;

    $instance->fcn = array('db', 'a');
    $instance->invoke();
    $instance->fcn = array('db', 'b');
    $instance->invoke();

语法很花哨,但它有效

//编辑:从您的评论中看起来最简单的选择是将方法名称作为字符串传递,就像这样

 class Paginator {
      function __consturct($db, $counter_func) ...

      function get_count() {
            $args = func_get_args();
            return call_user_func_array(
                  array($this->db, $this->counter_func),
                  $args);
      }
 }

 new Paginator($db, 'get_num_products');

答案 3 :(得分:0)

您是否在询问PHP是否具有功能参考?它没有。但它确实允许您通过将其名称放在字符串或类名称和方法名称的数组中来调用函数。 有关说明,请参阅call_user_func()variable functions

答案 4 :(得分:0)

我猜你在这里使用的是php。 Php支持variable functions这可能解决你的问题,但据我所知,不支持委托/函数指针。

您使用的数据库是什么?如果在您使用的数据库中支持,我会反对在代码中放置查询并使用存储过程作为替代方法。这可以解决您遇到的根本问题。

答案 5 :(得分:0)

class A {
   function a(){ echo 'I run a query';}
   function b(){ echo 'im the other query';}
}

class B {

    function Test() {
        invoke(new $A(), "a");
    }

    function invoke($obj, $method){
        //  Non static call
        call_user_func( array( $obj, $method ) );

        //  Static call
        //call_user_func( array( 'ClassName', 'method' ) );
    }
} 

我希望这会有所帮助。

答案 6 :(得分:0)

class DB {
   function a(){ echo 'I run a query';}
   function b(){ echo 'im the other query';}
}

class B {

    protected $db;
    private $method;

    function __constructor($db) { $this->db; }

    function invoke($m){
        $this->method = $m;
        //  Non static call
        call_user_func( array( $this->db, $this->method ) );
    }
}

$db = new DB();

$b = new B($db);
$b->invoke('a');

我对我的初步答案做了一些修改。你也可以查看这篇文章,它可能会有所帮助:

Database and OOP Practices in PHP

答案 7 :(得分:0)

您需要进行此更改:

$arguments = array('apple','banana','pineapple');
$a = new A();
$use_function = array(&$a,"a"); // Make this changes to your code

$instance = new B();
$instance->invoke($use_function,$arguments);

答案 8 :(得分:0)

观察者设计模式可能对此类事物有用,或者可能是对模式的误用;我还不知道。无论如何,供您考虑:

class DbObserver implements SplObserver
{
   public function update(SplSubject $subject)  // Required
   {
       $method = $subject->getObserverMethod();
       $args   = $subject->getObserverArgs();
       $this->$method($args);
   }

   private function a($args) 
   {
       echo 'I run query ' . $args[0] . '<br />';
   }

   private function b($args)
   {
       echo 'I run query ' . $args[0] . ' because ' . $args[1] . '<br />';
   }

   private function c() 
   {
       echo 'I have no argument' . '<br />';
   }
}

class ObserverObserver implements SplObserver
{
    public function update(SplSubject $subject)  // Required
    {
        if (count($subject->getAttached()) > 1) {
            echo 'I saw that<br />';
        } else {
            echo 'Nothing happened<br />';
        }
    }
}

class DbSubject implements SplSubject
{
    private $observerMethod;
    private $observerArgs = array();
    private $attached     = array();

    public function notify()  // Required
    {
        foreach ($this->attached as $each) {
            $each->update($this);
        }
    }

    public function attach(SplObserver $observer)  // Required 
    {
        $this->attached[] = $observer;
    }

    public function detach(SplObserver $observer)  // Required
    {
        $key = array_keys($this->attached, $observer);
        unset($this->attached[$key[0]]);
    }

    public function setObserverMethod($method, $args = array())
    {
        $this->observerMethod = $method;
        $this->observerArgs = $args;
        return $this;
    }

    public function getObserverMethod()
    {
        return $this->observerMethod;
    }

    public function getObserverArgs()
    {
        return $this->observerArgs;
    }

    public function getAttached()
    {
        return $this->attached;
    }
}

$db_subj = new DbSubject;
$db_obs  = new DbObserver;
$db_subj->attach($db_obs);
$args = array('A');
$db_subj->setObserverMethod('a', $args)->notify();
$args = array('B', 'I can');
$db_subj->setObserverMethod('b', $args)->notify();
$obsvr = new ObserverObserver;
$db_subj->attach($obsvr);
$db_subj->setObserverMethod('c')->notify();
$db_subj->detach($db_obs);
$db_subj->notify();

/**
I run query A
I run query B because I can
I have no argument
I saw that
Nothing happened
**/