【问题标题】:Global PHP functions chaining through a Class通过类链接的全局 PHP 函数
【发布时间】:2011-05-11 13:47:43
【问题描述】:

是否可以通过一个对象/类链接所有 PHP 函数?

我有这个想法,我想象它是这样的:

$c = new Chainer();

$c->strtolower('StackOverFlow')->ucwords(/* the value from the first function argument */)->str_replace('St', 'B', /* the value from the first function argument */);

这应该产生:

Backoverflow

谢谢。

【问题讨论】:

  • “所有 PHP 函数”是什么意思?所有内置的 PHP 函数,例如字符串、数组、mysql 函数?我认为这是不可能的,因为每个函数都应该返回一个对象。
  • 这样做比$c->strtolower(ucwords(str_replace('St','B','StackOverFlow'))) 有什么优势?可读性?

标签: php methods chaining


【解决方案1】:

看看:

http://php.net/manual/en/language.oop5.magic.php

特别是:

http://www.php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.methods

并且可能:

http://php.net/manual/en/function.call-user-func-array.php

因为有很多人发布了他们的示例,我也会尝试:

<?php
class Chainer
{
    protected $buffer = null;

    public function __call($name, $args) {
        if (method_exists($this, $name)) {
            $this->buffer = call_user_func_array(array($this, $name), $args);
        }
        elseif (function_exists($name)) {
            if ($this->buffer !== null) {
                $args[] = $this->buffer;
            }

            $this->buffer = call_user_func_array($name, $args);
        }

        return $this;
    }

    public function strpos($needle, $offset = 0) {
        return strpos($this->buffer, $needle, $offset);
    }

    public function __toString() {
        return (string)$this->buffer;
    }
}

$c = new Chainer();
echo $c->strtolower('StackOverFlow')->ucwords()->str_replace('St', 'B')->strpos('overflow'); // output: 4

【讨论】:

  • 是的!这是我真正需要的。直到现在才完全理解那些神奇的功能。
  • 这将在第一个不接受主题参数作为最后一个参数的函数上中断
  • 正确。这只是如何使用__call 的一个示例。
  • 是的,你说的 100% 是对的,但我总能以某种方式过滤;)
  • 我更改了示例代码,以包含如何处理特殊情况的想法。 (参见 strpos 和 method_exists 部分)
【解决方案2】:

你的意思是str_replace('St', 'B', ucwords(strtolower('StackOverFlow')))

您在上面调用的方法是函数,而不是绑定到任何类的方法。 Chainer 必须实现这些方法。如果这是您想要做的(可能出于不同的目的,这只是一个示例),您的 Chainer 实现可能如下所示:

class Chainer {
   private $string;
   public function strtolower($string) {
      $this->string = strtolower($string);
      return $this;
   }
   public function ucwords() {
      $this->string = ucwords($this->string);
      return $this;
   }
   public function str_replace($from, $to) {
      $this->string = str_replace($from, $to, $this->string);
      return $this;
   }
   public function __toString() {
      return $this->string;
   }
}

这在你上面的例子中有点用,但你可以这样称呼它:

$c = new Chainer;
echo $c->strtolower('StackOverFlow')
   ->ucwords()
   ->str_replace('St', 'B')
; //Backoverflow

请注意,您永远不会从链中取出 /* the value from the first function argument */ 的值,因为这没有任何意义。也许你可以用一个全局变量来做,但这会很可怕。

重点是,您可以通过每次返回$this 来链接方法。对返回的值调用下一个方法,该值是同一个对象,因为您返回了它(返回 $this)。了解哪些方法启动和停止链很重要。

我认为这种实现最有意义:

class Chainer {
   private $string;
   public function __construct($string = '') {
      $this->string = $string;
      if (!strlen($string)) {
         throw new Chainer_empty_string_exception;
      }
   }
   public function strtolower() {
      $this->string = strtolower($this->string);
      return $this;
   }
   public function ucwords() {
      $this->string = ucwords($this->string);
      return $this;
   }
   public function str_replace($from, $to) {
      $this->string = str_replace($from, $to, $this->string);
      return $this;
   }
   public function __toString() {
      return $this->string;
   }
}
class Chainer_empty_string_exception extends Exception {
   public function __construct() {
      parent::__construct("Cannot create chainer with an empty string");
   }
}

try {
   $c = new Chainer;
   echo $c->strtolower('StackOverFlow')
      ->ucwords()
      ->str_replace('St', 'B')
   ; //Backoverflow
}
catch (Chainer_empty_string_exception $cese) {
   echo $cese->getMessage();
}

【讨论】:

  • @Phil 只是一个巧合,但并不难弄清楚。我实际上不喜欢您的实现,因为您允许稍后通过其他方法设置字符串,这对我来说毫无意义。
【解决方案3】:

只要Chainer 类看起来像这样,您就可以这样做

class Chainer
{
    private $string;

    public function __construct($string = null)
    {
        $this->setString($string);
    }

    public function setString($string)
    {
        $this->string = $string;
        return $this;
    }

    public function __toString()
    {
        return $this->string;
    }

    public function strtolower($string = null)
    {
        if (null !== $string) {
            $this->setString($string);
        }
        $this->string = strtolower($this->string);
        return $this;
    }

    public function ucwords($string = null)
    {
        if (null !== $string) {
            $this->setString($string);
        }
        $this->string = ucwords($this->string);
        return $this;
    }

    public function str_replace($search, $replace, $string = null)
    {
        if (null !== $string) {
            $this->string = $string;
        }
        $this->string = str_replace($search, $replace, $this->string);
        return $this;
    }
}

不过对我来说看起来很傻。

您也许可以合并一个神奇的 __call 方法,但处理存储的变量和可选方法参数将是一个很大的难题。

【讨论】:

  • @metadata Pfft,为什么alot得到了所有的赞誉?
猜你喜欢
  • 2011-01-15
  • 2013-10-30
  • 2013-02-21
  • 1970-01-01
  • 2013-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-11
相关资源
最近更新 更多