将函数传递给类 [英] Pass functions to a class

查看:157
本文介绍了将函数传递给类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

PHP
mysql database
我在这里创建了一个问题的跟踪,这是具体关于分页

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

I need to call a method from one class in another, and be able to change the method that is called. Like so

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'方法中的db类中的方法' '
感谢

I want to use a method 'a' from the db class in the 'yourClass' method 'invoke' Thanks

好的,这是我从提供的答案放在一起,它的工作原理。

Ok this is what i have put together from the answers provided and it works.

    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'到屏幕

Which writes, 'Method a is used' to the screen

但我真的想能够传递参数到方法a所以我试过下面的代码。

But i really want to be able to pass arguments to method "a" so i tried the code below.

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);

它几乎可以工作,但我得到这些错误上面的正确答案

It almost works but i get these errors above the correct answer

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

Missing argument 1 for A::a(),.....for argument 2 and 3 as well but then the answer prints to the screen "the three passed values are apple and banana and pineapple"

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

I'm probably making a rookie mistake I've been coding all day. If someone could fix the script above and submit the working code, I would be eternally grateful. I have to put this issue to bed so i can go to bed. Thanks

推荐答案

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

As of PHP5.3 you could use closures or functors to pass methods around. Prior to that, you could write an anonymous function with create_function(), but that is rather awkward.

基本上,您要做的事情可以使用

Basically, what you are trying to do could be done with the Strategy Pattern.

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

除此之外,您可能需要查看Fowlers的数据源架构模式 Zend Framework (以及几乎所有其他PHP框架)提供数据库访问类,您还可以使用这些模式,还有一个 paginator class ,所以为什么不检查他们来了解他们是如何做到的。

Apart from that, you might want to look into Fowlers's Data Source Architectural Patterns. The Zend Framework (and pretty much all other PHP frameworks) offers database access classes you could use for these patterns and there is also a paginator class, so why not check them out to learn how they did it.

删除了EDIT 1,因为它在OP更改问题之后不再有用(见wiki)。

removed EDIT 1 as it wasnt helpful anymore after the OP changed the question (see wiki)

EDIT 2
好​​的,让我们一步一步来解决这个问题(不使用策略模式)

EDIT 2 Ok, let's take a step by step approach to this (not using a Strategy Pattern though)

这个问题可以很容易地用这个代码解决:

What you are asking for in the question can easily be solved with this code:

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。:

With this code you'd just call the appropriate methods. No setting of methods names. No clumsy extra invoke method. No reinventing of how methods are called. You dont need it, because PHP has the __call function that you just taught to send all methods not existing in MyInvoker to $myObject, e.g. Foo.:

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

你可能只是将MyInvoker扩展为Foo的子类,例如

You might just as well have extended MyInvoker to be a subclass of Foo, e.g.

class MyInvoker extends Foo {}

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

and then you could do the same. This not what you need though and it illustrates how pointless it is, to do such a thing. MyInvoker now does nothing by itself. It is an empty class and effectively the same as Foo. Even with the previous approach using the __call method it is not doing anything. This is why I have asked you to be more specific about the desired outcome, which is a 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);
    }
}


$ b $ p

使用该代码,我们的Paginator本身,紧密耦合数据库连接类和所有查询,它允许您调用这些

With that code, our Paginator creates everything it needs inside itself, tightly coupling the db connection class and all queries and it allows you to call upon these, like so

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

然后发生的是,Paginator会识别它不知道getImageCount(),并将调用__call方法。 __call方法将尝试调用DbQueries上的getImageCount()方法。因为它存在,它将返回查询,然后将其传递到db连接以执行它。伟大的你会说,但它不是。事实上,这是可怕的。您的分页器的职责是计算表中的项目,并从该表中获取一定范围和数量的项目。但现在,它不做这样的事情。它完全忽视了什么,所以让我们尝试一个新的类:

What is happening then is, Paginator will recognize it doesnt know getImageCount() and will invoke the __call method. The __call method will try to invoke the getImageCount() method on the DbQueries. Since it exists, it will return the query, which in turn is passed to the db connection to execute it. Great you'd say, but it's not. In fact, this is horrible. Your paginator's responsibility is to count items in a table and fetch items from this table in a certain range and amount. But right now, it is not doing anything like this. It is completely oblivious to whats going on, so lets try a new class:

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是一个聚合而不是一个复合,意味着它不实例化自身内部的对象,但需要它们在构造函数中传递给它。这称为依赖注入(还提供了一个松耦合。 phprel =nofollow noreferrer> interface ),这将使你的应用程序更易于维护,因为它很容易现在交换组件。在对您的代码进行单元测试时,此操作也会派上用场。

Much better. Now our Paginator is an aggregate instead of a composite, meaning it does not instantiate objects inside itself, but requires them to be passed to it in the constructor. This is called dependency injection (and also provides a loose coupling, when dbConn uses an interface) which will make your app much more maintainable, as it is easy to exchange components now. This will also come in handy when Unit Testing your code.

此外,您的Paginator现在集中在它应该做什么:计数和获取任意查询的项目。不需要传递方法。不需要模糊的方法调用。您可以这样使用:

In addition, your Paginator now concentrates on what it is supposed to do: counting and fetching items of an arbitrary query. No need to pass methods around. No need for obscure method invocation. You'd use it like this:

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

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

And that's it. Well, almost. You'd have to render the pagination of course. But this should be rather trivial, if you extend what you already have above. The $imageCount property is a hint at where to go next.

无论如何,希望我可以散发一些光。

Anyway, hope that I could shed some light.

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

P.S. The $this->dbConn->query($sql, $query) calls are of course dummy code. Dont expect to be able to copy and paste it and get it working. In addition, you should make sure the queries added to the Paginator SQL is safe to use. You wouldnt want someone to insert a query that deletes all your db rows. Never trust user input.

P.P.S。 $ query 应为SQL查询字符串。请查看PHP手册中的 PDO :: prepare 。一般来说,在执行语句之前,它可以提供更好的性能和安全性来准备语句。手册中的页面将为您提供有关查询调用中的线索。如果您不想使用PDO,只需使用 sprintf() str_replace()替换 $ query $ this-> dbConn-> query(sprintf('SELECT count(*)from(%s)',$ query)但请记住,准备声明的好处,并可能为 SQL注入漏洞敞开大门。

P.P.S. $query should be an SQL query string. Check the PHP manual for PDO::prepare. In general, it yields better performance and security to prepare a statement before executing it. The page in the manual will give you the clues about the ? in the query calls. If you dont want to use PDO, just use sprintf() or str_replace() to replace ? with $query, e.g. $this->dbConn->query(sprintf('SELECT count(*) from (%s)', $query) but keep in mind that this has none of the benefits of a prepared statement and potentially opens the door for SQL Injection vulnerabilities.

PPPS 是,依赖注入一般是一个首选的策略,这是一个有利的主题,虽然可能太多,以至于不能完全掌握现在,但它是非常值得研究它现在,如果你试图支持聚集你的类只应该做他们负责,并通过构造函数获得任何依赖。

P.P.P.S Yes, Dependency Injection is generally a preferred strategy. This is an advanved topic though and might be too much to fully grasp right now, but it's well worth looking into it. For now, it should be enough if you try to favor favor aggregation over composition. Your classes should only do what they are responsible for and get any dependencies through the constructor.

这篇关于将函数传递给类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆