【问题标题】:PHP methods that work in both instantiated and static contexts?在实例化和静态上下文中工作的 PHP 方法?
【发布时间】:2012-05-13 15:13:35
【问题描述】:

我正在尝试设置一些可在实例化和静态上下文中调用的 PHP 方法。有什么好的方法可以做到这一点?例如我希望能够做到:

Foo::bar($item); 
foo($item)->bar();

我可以设置两个单独的类并让每个函数修改 thisArg 并委托给另一个,但似乎必须有更好的方法。我能想到的唯一方法是只用一个类来做这件事:

function foo($item = null) {
    return $item instanceof Foo ? $item : new Foo($item);
}

class Foo { 
    protected $wrapped;

    public function __construct($item = null) { 
        $this->wrapped = $item;
    }

    public function get() {
        return $this->wrapped;
    }

    public function bar($item = null) {
        isset($this) and $item = &$this->wrapped;
        // do stuff with $item
        return isset($this) ? $this : $item;
    }
}

如果您查看underscore.php 的代码,他们会执行类似的操作。我读过一些相关的questions from a while back,其中指出使用isset($this) 来确定上下文会引发警告,但它似乎工作正常......对此有任何更新的想法吗?另一种可能性是创建两个类,一个具有所有静态版本的方法,然后另一个类使用__call 委托给静态方法,例如:

class _Foo
{
        protected $wrapped;

        public function __construct($item = null) { 
            $this->wrapped = $item;
        }

        public function __call($method_name, $args) { 
            array_unshift($args, $this->wrapped);
            $this->wrapped = call_user_func_array('Foo::' . $method_name, $args);
            return $this;
        }


}

想法?

【问题讨论】:

    标签: php oop design-patterns methods static-methods


    【解决方案1】:

    http://www.php.net/manual/en/domdocument.loadxml.php 会执行此操作,但是当从静态上下文调用时,会发出 E_STRICT

    大致来说,制作一个静态和实例可调用的方法现在可以工作,但该功能可能会被删除。也许还有另一种方法可以完成您的需要?

    编辑: 正如您提到的,可以使用 __call 模拟该功能,而无需抛出 E_STRICT,但您不需要两个类:

    <? //PHP 5.4+
    class mightBeInstance
    {
        public function __call($name, $arguments)
        {
            if ($name === 'doSomething') {
                return static::doSomething($this);
            }
        }
    
        public static function doSomething(mightBeInstance $item = null)
        {
            if ($item === null) {
                $item = new static();
            }
    
            $item->didSomething = true; // Or whatnot
    
            return $item;
        }
    }
    
    var_dump(
        (new mightBeInstance)->doSomething(),
        mightBeInstance::doSomething()
    );
    
    ?>
    

    【讨论】:

    • 正是——这就是我要问的原因。
    • 见鬼,不过是伪装!如果我想让一个数据库类通常被称为静态单例,但在需要 2 个连接时能够实例化,该怎么办?
    • @Cory,但__call“仅在对象上下文中调用不可访问的方法时触发”。但是静态方法在对象上下文中是可用的,所以我认为除非该方法不存在,否则它不会触发。不过我喜欢这个主意。
    • @Cory 检查$item === null 仅在没有其他预期参数时才有效。
    • @YourCommonSense 我认为静态版本在您只需要调用一种方法并立即返回时特别有用。 OO 读取更清晰的 IMO,并且更好地调用链中的多个方法,例如 foo($item)-&gt;bar()-&gt;down()-&gt;the_street()-&gt;get()
    【解决方案2】:

    这是唯一可靠的解决方案。它适用于 5.3+(除了底部的内联对象实例化),但有点笨拙。

    class foo {
        protected function __call($method, $args) {
            if ($method == 'bar') {
                return $this->bar($args[0]);
            }
        }
        protected function bar($baz) {
            return "object context: $baz\n";
        }
    
        public static function __callStatic($method, $args) {
            if ($method == 'bar') {
                return self::barStatic($args[0]);
            }
        }
        protected static function barStatic($baz) {
            return "static context: $baz\n";
        }
    }
    
    echo foo::bar('baz');
    echo (new foo())->bar('baz');
    

    不推荐: 以下代码在 PHP 5.6 中有效,但在 PHP 中运行时会抛出 E_DEPRECATED 错误消息“不应静态调用非静态方法 foo::bar()” 7.0。问题不在于您所说的isset($this),而在于让一个函数执行双重任务:它要么是静态的,要么不是。 PHP 7.0 仍然支持它,但你不应该依赖它。

    class foo {
        public function bar($baz) {
            if (isset($this)) {
                return "object context: $baz\n";
            } else {
                return "static context: $baz\n";
            }
        }
    }
    
    echo foo::bar('baz');
    echo (new foo())->bar('baz');
    

    不工作: 这会在 PHP 5.6 和 PHP 7.0 中引发致命错误“无法重新声明 foo::bar()”,但如果您可以这样做,那将是理想的而是。

    class foo {
        public function bar($baz) {
            return "object context: $baz\n";
        }
        public static function bar($baz) {
            return "static context: $baz\n";
        }
    }
    
    echo foo::bar('baz');
    echo (new foo())->bar('baz');
    

    也许在未来的版本中,一旦删除了已弃用的用法,我们就可以做到这一点。

    【讨论】:

      猜你喜欢
      • 2015-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-19
      • 2021-12-05
      • 1970-01-01
      相关资源
      最近更新 更多