将函数传递给类

and*_*rew 2 php class

PHP mysql数据库我在这里创建了一个跟随问题,特别是关于分页

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

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();
Run Code Online (Sandbox Code Playgroud)

我想在'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");
Run Code Online (Sandbox Code Playgroud)

其中写道,"方法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);
Run Code Online (Sandbox Code Playgroud)

它几乎可以工作,但我得到的错误高于正确的答案

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

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

Gor*_*don 8

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

基本上,您尝试做的事情可以通过策略模式完成.

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

除此之外,您可能希望了解Fowlers的数据源架构模式.在Zend框架(和几乎所有其他的PHP框架)提供数据库访问类,你可以使用这些模式,并也有一个分页程序类,那么为什么不检查出来,了解他们是如何做到的.

删除编辑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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

$invoker = new MyInvoker;
$invoker->bar(); // outputs 'bar method in Foo called'
Run Code Online (Sandbox Code Playgroud)

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

class MyInvoker extends Foo {}
Run Code Online (Sandbox Code Playgroud)

然后你也可以这样做.这不是你所需要的,它说明了做这样的事情是多么无意义.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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

好多了.现在我们的Paginator是一个聚合而不是复合,这意味着它不会在其自身内实例化对象,而是要求它们在构造函数中传递给它.这称为依赖注入(并且当dbConn使用接口时也提供松耦合),这将使您的应用程序更易于维护,因为现在很容易交换组件.单元测试代码时,这也会派上用场.

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

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

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

无论如何,希望我能够解决一些问题.

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

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

PPPS是的,依赖注入通常是首选策略.这虽然是一个很有用的话题,但现在可能还不能完全掌握,但值得深入研究.现在,如果你试图赞成聚合而不是合成,那就足够了.您的类应该只执行它们负责的内容并通过构造函数获取任何依赖项.