是否有可能覆盖PHP中的函数


75

你能声明这样的函数吗?

function ihatefooexamples(){
  return "boo-foo!";
};

然后像这样重新声明它...

if ($_GET['foolevel'] == 10){
  function ihatefooexamples(){
    return "really boo-foo";
  };
};

这样可以覆盖函数吗?

可以吗




如果扩展PHP以使“ unset”可用于删除准备使用“ function”重新定义的函数定义,那将是很棒的。
David Spector

Answers:


103

编辑

要解决此答案未直接解决原始问题的评论。如果您来自Google搜索,请从此处开始

有一个名为override_function的函数可以实际使用。但是,由于此函数是Advanced PHP Debugger扩展的一部分,因此很难override_function()为生产使用提供参数。因此,我要说“否”,不可能以原始提问者的意图来覆盖函数。

原始答案

在这里,您应该利用OOP,特别是多态性。

interface Fooable
{
    public function ihatefooexamples();
}

class Foo implements Fooable
{
    public function ihatefooexamples()
    {
        return "boo-foo!";
    }
}

class FooBar implements Fooable
{
    public function ihatefooexamples()
    {
        return "really boo-foo";
    }
}

$foo = new Foo();

if (10 == $_GET['foolevel']) {
    $foo = new FooBar();
}

echo $foo->ihatefooexamples();

18
+1解决实际问题,而不是简单地尝试回答确切的问题...
ircmaxell 2010年

2
如果构造函数(a)昂贵,或者(b)有副作用,我唯一要做的更改就是将其放在$foo = new Foo()一个else块中。
奥斯丁·海德

2
@戈登您正在过度分析。我认为这很明显只是一个例子。
彼得·贝利

7
我不同意这个答案,这个问题清楚地说是“功能”,而不是“方法”,很明显上下文不是类。我是从Google搜索来到这里的,此刻我正在使用非OO旧式库,这个答案毫无用处。
mastazi

4
@mastazi感谢您向我指出,这是Google搜索该问题的最佳答案。我已经用信息更新了答案,该信息应该可以帮助将来寻求与您目标相似的求职者。
彼得·贝利

73

命名空间php> = 5.3中的Monkey补丁

猴子补丁是一种比修改解释器更容易避免的方法。

猴子补丁是用您自己的类似“补丁”代替实际实现的艺术。

忍者技能

在您像PHP Ninja这样的猴子补丁之前,我们首先必须了解PHP名称空间。

从PHP 5.3开始,我们就引入了名称空间,乍一看,它们可能等同于java包之类的名称空间,但并不完全相同。在PHP中,命名空间是一种通过创建焦点层次结构(尤其是对于函数和常量)来封装范围的方法。作为本主题,回退到全局功能旨在说明。

如果调用函数时未提供名称空间,则PHP首先在当前名称空间中查找,然后向下移动层次结构,直到找到在该前缀名称空间中声明的第一个函数并执行该功能。对于我们的示例,如果您print_r();要从namespace My\Awesome\Namespace;PHP的作用中进行调用,则首先要查找一个称为的函数,My\Awesome\Namespace\print_r();然后My\Awesome\print_r();My\print_r();寻找该函数,直到在全局名称空间中找到PHP内置函数为止\print_r();

您将无法定义 function print_r($object) {}在全局命名空间中,因为这将导致名称冲突,因为具有该名称的函数已经存在。

期望出现致命错误,例如:

Fatal error: Cannot redeclare print_r()

但是,没有什么可以阻止您在命名空间范围内仅执行此操作。

修补猴子

假设您有一个使用多个脚本 print_r();调用。

例:

<?php
     print_r($some_object);
     // do some stuff
     print_r($another_object);
     // do some other stuff
     print_r($data_object);
     // do more stuff
     print_r($debug_object);

但是后来您改变了主意,而是希望将输出包装在<pre></pre>标签中。你曾经发生过吗?

在您进行更改之前,请更改每个呼叫,以print_r();考虑使用猴子修补程序。

例:

<?php
    namespace MyNamespace {
        function print_r($object) 
        {
            echo "<pre>", \print_r($object, true), "</pre>"; 
        }

        print_r($some_object);
        // do some stuff
        print_r($another_object);
        // do some other stuff
        print_r($data_object);
        // do more stuff
        print_r($debug_object);
    }

您的脚本现在将使用MyNamespace\print_r();而不是全局\print_r();

非常适合模拟单元测试。

欢乐!


3
一个非常有价值的方法!感谢您的分享
Nico Haase

1
对于那些对传统的非oo库感到困惑的人来说,这是一个非常有用的答案!
mastazi

1
当在现有的大型包含文件中定义函数A,B和C(我不想更改它们,因为其他页面使用它们)并且我只想覆盖主函数B时,我无法执行此操作文件,其中B在单独的包含文件中定义。我猜想命名空间在这种情况下不起作用。
David Spector

@DavidSpector您需要在单独的包含文件ex中重新声明要进行命名的名称空间。namespace SeparateInclude {}然后在主文件中可以再次命名空间namespace MainFile {},包括新文件function B()再次在调用的主文件中创建猴子补丁\SeparateInclude\B();B();现在,当您调用主文件时,\MainFile\B();它将调用\SeparateInclude\B();成功避免\B();从大/原始包含文件中调用的文件。
尼克

它实际上是否因为似乎没有而沿名称空间移动?似乎A \ B \ C尝试使用“ f”将仅检查\ A \ B \ C \ f或\ f(“顶级f”),\的字面意思是带有\的开头,只有\特殊含义(例如,是否将当前名称空间连接为前缀,或者不这样做)-您可以确认吗?因为这样做会很有意义!但这是PHP,所以他们不会这样做(我已经尝试并广泛阅读,这给了我一线希望-暗恋或让我看看我想念的东西!PHP5将是回答BTW的好选择)
Alec Teal

25

看一下override_function重写功能。

override_function —覆盖内置函数

例:

override_function('test', '$a,$b', 'echo "DOING TEST"; return $a * $b;');

2
这是一个好主意,但是如果函数很大,那么传递这么长时间的字符串就不好了!:(,还有一点是,这只能用于内置函数,不能由用户定义!
RobertPitt 2010年

@RobertPitt:同意这是我们目前所拥有的,您的解决方案看起来不错,但这取决于所使用的php版本。
Sarfraz

为什么不希望使用<5.3.0以下的php版本?除非您构建一个可以零售/发布给公众的脚本,否则您有一个有效的论点。
罗伯特·皮特(RobertPitt)2010年

@RobertPitt:我使用的是5.3,希望OP也使用相同的方法:)
Sarfraz 2010年

1
我尝试使用覆盖功能,但它需要先配置PECL和PECL的一部分。

13

简短的回答是不,一旦在PHP函数范围内,您就无法覆盖该函数。

您最好使用这样的匿名函数

$ihatefooexamples = function()
{
  return "boo-foo!";
}

//...
unset($ihatefooexamples);
$ihatefooexamples = function()
{
   return "really boo-foo";
}

http://php.net/manual/zh/functions.anonymous.php


您真的需要unset吗?我知道这样做并不坏,只是很好奇(我没有尝试过这种特殊情况)...
ircmaxell 2010年

2
@ircmax您不必使用unset
NullUserException 2010年

我对匿名函数一无所知,我只是知道它们如何创建它们,但我从未使用过它们,我知道如果过度使用unset可能会很昂贵,但是取决于函数的大小,它可能会更好!
罗伯特·皮特(RobertPitt)2010年

1
我不知道为什么这没有得到要求的好处。这是最干净,可用和明智的方法。OP并没有要求使用OOP方法(实际上恰恰相反),并且第三方功能根本无法工作,因此这是唯一的解决方案。
基督教徒

这并不覆盖任何功能,而只是改变变量的值。您将获得完全相同的结果,而完全省略了闭包(匿名函数)部分。这不能回答问题!
尼克

11

您不能在PHP中重新声明任何功能。但是,您可以覆盖它们。检出覆盖功能以及重命名功能,以便在需要时保存要覆盖的功能。

因此,请记住,当您覆盖某个函数时,就会丢失它。您可能要考虑保留它,但使用其他名称。只是说。

另外,如果这些是您要覆盖的类中的函数,则只需创建一个子类并在您的类中重新声明该函数,而不必执行rename_function和override_function。

例:

rename_function('mysql_connect', 'original_mysql_connect' );
override_function('mysql_connect', '$a,$b', 'echo "DOING MY FUNCTION INSTEAD"; return $a * $b;');

16
这需要APD PECL扩展,并且已经过时。最新版本为2004年。pecl.php.net
package/

5

我会在一个include文件中包含一个案例的所有功能,而在另一个文件中包含其他功能include

例如simple.inc将包含function boofoo() { simple }并且really.inc将包含function boofoo() { really }

它具有相同种类的所有功能,可帮助您提高程序的可读性/维护性。 inc

然后在主模块的顶部

  if ($_GET['foolevel'] == 10) {
    include "really.inc";
  }
  else {
    include "simple.inc";
  }

如果需要(似乎不是必需的),可以在包含文件的顶部添加$iam = "really"$iam = "simple"
e2-e4

1
我认为这是针对此问题的最佳非OOP答案,因为它显示了状态模式的程序实现。(尽管我更喜欢将ifs替换为foreach循环的实现。)我已经成功使用了这种模式。您可以找出日志记录使用的功能。日志消息具有在生产或验收环境中不再使用的级别。
Loek Bergman



1

针对相关情况的解决方案,其中有一个包含文件A,您可以对其进行编辑,并希望在包含文件B(或主文件)中覆盖其某些功能:

主文件:

<?php
$Override=true; // An argument used in A.php
include ("A.php");
include ("B.php");
F1();
?>

包含文件A:

<?php
if (!@$Override) {
   function F1 () {echo "This is F1() in A";}
}
?>

包含文件B:

<?php
   function F1 () {echo "This is F1() in B";}
?>

浏览到主文件将显示“这是B中的F1() ”。


0

根据需要的情况,也许可以使用如下匿名函数:

$greet = function($name)
{
    echo('Hello ' . $name);
};

$greet('World');

...然后您可以随时将新函数设置为给定变量

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.