【问题标题】:Store but not EXECUTE a Global Function (PHP)存储但不执行全局函数 (PHP)
【发布时间】:2019-11-14 18:04:36
【问题描述】:

这是一个奇怪的情况,我认为答案是“你不能那样做,你在想什么?”但希望有人能证明我错了。

我的目标是将全局范围的函数存储在变量中,然后将其注入到类对象中执行。

  • 我想避免使用 call_user_func(),因为它会在全局命名空间中搜索函数,效果与我只是从类对象中执行全局函数相同。我希望我的类执行该对象,就好像它是一个内部类方法,而不是一个外部函数。 This 接近但不完全。
  • 我无法修改函数或将其包装在一个类中。
  • (为什么我要跳过这些圈子?)需要在这个类中使用以遵循规范。

我知道我可以在类中复制该函数并完成它,但你知道这样做的问题(加上它在 SRP 上蔓延。)反射可以完美地工作,但这个函数不在一个类中,它就在一个包含中。我尝试将其包装为匿名函数,而闭包对象不执行该函数。

有希望做到这一点吗?该函数很简单,接受一个标量参数,对其执行一些操作,返回一个值(并且与其他代码紧密耦合,无法移动或更改。)

function someFunction($param)
{
  // do some stuff
  return $someScalarValue;
}

我希望是这样的

$func = someFunction([some value]); // doesn't work of course, this would store result in $func
$cls = new SomeClass($func);

然后类中的方法可以运行函数对象,很像 call_user_func 但不必搜索全局命名空间。

protected function someThing()
{
  $this->injected_function([some class value]); // also doesn't work of course
}

【问题讨论】:

  • 您的方法是正确的。实际上,您可以传递存储在变量中的函数。看我的回答。
  • 尚不清楚为什么链接的答案对您不起作用。 call_user_func 接受callable,因此不需要“搜索全局空间”。
  • 对不起。我没有测试就从头开始写答案。我已经修复了我的代码。希望现在对您有所帮助。
  • @yivi 谢谢,但是函数本身在全局命名空间中——它实际上没有调用 someFunction() 吗?我到处搜索 call_user_func() 是如何执行的,但不清楚它实际上是从全局命名空间还是在类本身中运行。谢谢你们的回复,现在正在修修补补。也感谢其他人的反对。 :-\
  • 直接传递一个方法怎么样?我想我没有详细理解你的问题。已针对其他建议更新答案。

标签: php function oop dependency-injection


【解决方案1】:

当您使用$this 时,您处于对象实例范围内。您可以将(引用)方法传递给构造函数。

$myFunc = function($arg) { var_dump($arg); return 314; };

class myClass {

  private $func;

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

  public function do($value) {
      $this->func->call($this, $value);
  }

}

$var = 'Hello world!';

$myObj = new myClass($myFunc);
$value = $myObj->do($var); // $value is now 314

如果您不希望将函数存储在全局命名空间中,您甚至可以即时传递这样的匿名函数:

$myObj = new myClass(function($arg) { var_dump($arg); return 314; });
$value = $myObj->do($var); // $value is now 314

【讨论】:

  • 好的,我是从零开始写的。我会测试一下。
  • @yivi 已修复。很抱歉之前没有测试。
  • 谢谢,但正如我在原始帖子中提到的那样,它存储了一个匿名函数 - 这是一个现有函数。当我做 $myFunc = someFunction($arg);它在注入前立即执行。这实际上是问题所在。 :-) 不过,要对其进行更多修改,这必须在某个地方找到解决方案。
  • 再次感谢,在这里合并cmets:“直接传递一个方法怎么样?” cmets 中的空间有限。我看到您已将匿名函数移到构造函数调用中,与 $myVar 相同。 . .想象一下我们正在尝试慢慢现代化的遗留系统。这是一个在全局空间中具有上述“someFunction()”的包含文件。在我们新创建的类对象中,我们需要访问这个函数,但它破坏了单元测试,因为这个外部函数进行了 DB 调用。我们在类中需要这个函数,所以我们可以模拟它。更有意义吗?
  • 你需要的是某种“注册表”或“依赖注入”。
【解决方案2】:

感谢 @Markkus Zeller 的 cmets,因为我怀疑没有办法完成我最初的任务,将全局函数作为依赖项“注入”。有,但它只适用于匿名函数。

在经历了很多压力后,我说服了我们的经理,将其封装在一个简单的类中是可行的方法。这个,

// require_once('some-function.php');

function someFunction($param)
{
  // do some stuff
  return $someScalarValue;
}

。 . .现在变成了这个。 (即时输入,可能包含缺陷,仅概念)

// require_once('some-function.php');

require_once('path/to/SomeFunctionClass.php');
function someFunction($param)
{
  $cls = new SomeFunctionClass($param);
  return $cls->execute();
}

。 . .其中 execute() 包含与 someFunction() 中相同的代码。我现在可以将“SomeFunctionClass”用于 DI。故事还有更多内容,但这就是要点,并且可以在不修改使用此全局函数的 450 多个实例中的任何一个的情况下实现这一更改(并且每个实例都可以逐渐移植以使用新的包装器。 ) 它还允许我隔离和模拟单元测试的功能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-08
    • 2013-08-16
    • 2012-09-17
    • 2016-04-11
    • 2020-11-04
    • 1970-01-01
    • 2011-09-14
    相关资源
    最近更新 更多