【问题标题】:PHP mandatory function callPHP强制函数调用
【发布时间】:2013-03-02 00:18:03
【问题描述】:

我知道可以使用接口来强制定义函数,但我找不到可以强制调用函数的东西,例如如果我使用函数创建一个作为另一个类成员的类(通过扩展等),则该类会自动确保使用该函数部分调用强制函数。

我的意思是,进一步澄清:

class domain {

   function isEmpty($input) {
      //apply conditional logic and results
   }
}
class test extends domain {

   function addTestToDBTable($test) {
      /** 
       *  try to add but this class automatically makes it so that all rules of
       *  class domain must be passed before it can run
       *  - so essentially, I am no longer required to call those tests for each and
       *    every method 
       **/  
   }
 }

如果这看起来不连贯,我们深表歉意。当然,这似乎很懒,但我希望能够强制上下文而不必担心

更新:

好的,进一步澄清:在 PHP 中,如果我为子类扩展并声明 __construct(),则该子类将覆盖父类 __construct()。我不希望这样,我希望父构造保留下来,并随心所欲地授权,就像子类也可以这样做一样。

【问题讨论】:

  • 你是指接口还是抽象类?
  • 也许你可以在domain 中创建一个addTestToDBTable(或其他函数名),然后使用parent::addTestToDBTable()
  • 我的意思是,例如,如果没有应用规则,我希望它自动输出错误——我希望强制执行规则,而不必将其应用于每个单独的函数。我希望该超类自动强制执行这些规则(例如默认或自定义集)。
  • @user784446 我认为这就是单元测试的目的。
  • @Rocket Hazmat,我明白,但我的意思是我什至不想这样做。我希望域自动处理所有这些东西。很简单,即使应用于域的上下文,测试也应该只关心自己——除非我另有说明。该类的每个函数都不应该有任何与域有关的显式代码,因此 addTestToDBTable 只是有它的逻辑。因为类测试是域的成员,域通过强制规则来管理它。

标签: php oop class function logic


【解决方案1】:

我想这可以通过两种不同的方式完成。

面向方面的编程

看看这里https://github.com/AOP-PHP/AOP

生成或编写代理类

一个非常简单的例子可能是:

<?php
class A {
    public function callMe() {
        echo __METHOD__ . "\n";
    }
}

class B extends A {

    // prevents instantiation
    public function __construct() {
    }

    public function shouldCallMe() {
        echo __METHOD__ . "\n";
    }

    public static function newInstance() {
        return new ABProxy();
    }
}

class ABProxy {
    private $b;

    public function __construct() {
        $this->b = new B();
    }

    public function __call($method, $args) {
        $this->b->callMe();
        return call_user_func_array(array($this->b, $method), $args);
    }
}

// make the call
$b = B::newInstance();
$b->shouldCallMe();

// Outputs
// ------------------
// A::callMe
// B::shouldCallMe

希望这会有所帮助。

【讨论】:

    【解决方案2】:

    听起来你想要一个Decorator

    请参阅 This answer 了解如何操作的详细说明。请注意,它不需要类扩展。

    【讨论】:

      【解决方案3】:

      我会使用带有一些文档块元编程魔法的域验证装饰器。但这确实是整个图书馆的工作,毫无疑问是存在的。

      fiddle

      <?php
      class FooDomain {
          public static function is_not_empty($input) {
              return !empty($input);
          }
      }
      
      class Foo {
          /**
           * @domain FooDomain::is_not_empty my_string
           */
          public function print_string($my_string) {
              echo $my_string . PHP_EOL;
          }
      }
      
      $foo = new DomainValidator(new Foo());
      $foo->print_string('Hello, world!');
      try {
          $foo->print_string(''); // throws a DomainException
      } catch (\DomainException $e) {
          echo 'Could not print an empty string...' . PHP_EOL;
      }
      
      // ---
      
      class DomainValidator {
          const DOMAIN_TAG = '@domain';
      
          private $object;
      
          public function __construct($object) {
              $this->object = $object;
          }
      
          public function __call($function, $arguments) {
              if (!$this->verify_domain($function, $arguments)) {
                  throw new \DomainException('Bad domain!');
              }
      
              return call_user_func_array(
                  array($this->object, $function),
                  $arguments
              );
          }
      
          public function __get($name) {
              return $this->object->name;
          }
      
          public function __set($name, $value) {
              $this->object->name = $value;
          }
      
          private function verify_domain($function, $arguments) {
              // Get reference to method
              $method = new \ReflectionMethod($this->object, $function);
              $domains = $this->get_domains($method->getDocComment());
              $arguments = $this->parse_arguments(
                  $method->getParameters(),
                  $arguments
              );
              foreach ($domains as $domain) {
                  if (!call_user_func(
                      $domain['name'],
                      $arguments[$domain['parameter']]
                  )) {
                      return false;
                  }
              }
              return true;
          }
      
          private function get_domains($doc_block) {
              $lines = explode("\n", $doc_block);
              $domains = array();
              $domain_tag = DomainValidator::DOMAIN_TAG . ' ';
              foreach ($lines as $line) {
                  $has_domain = stristr($line, $domain_tag) !== false;
                  if ($has_domain) {
                      $domain_info = explode($domain_tag, $line);
                      $domain_info = explode(' ', $domain_info[1]);
                      $domains[] = array(
                          'name'      => $domain_info[0],
                          'parameter' => $domain_info[1],
                      );
                  }
              }
              return $domains;
          }
      
          private function parse_arguments($parameters, $values) {
              $ret = array();
              for ($i = 0, $size = sizeof($values); $i < $size; $i++) {
                  $ret[$parameters[$i]->name] = $values[$i];
              }
              return $ret;
          }
      }
      

      输出:

      Hello, world!
      Could not print an empty string...
      

      【讨论】:

      • 抱歉,我必须更彻底地阅读。但是,我已经更深入地考虑了它,父类必须能够“知道”子类的方法,或者子类必须能够“知道”它自己的方法(即对于构造函数来注册其方法)。我真的不知道该怎么做。
      • @user784446 PHP 有反射,但速度很慢,所以我不会在生产过程中使用它。您可以通过making a \ReflectionClass 和调用its getMethods method 获取类的方法。真正的问题是不可能拦截所有方法调用(只有那些不存在的方法调用),所以你要么必须将所有方法设为私有,要么必须使用装饰器。
      • @walheed khan 我可以使用 get_class_methods($class) 但仍然存在类构造的问题。
      • @user784446 如果父类和子类都知道子类的方法,问题出在哪里?
      猜你喜欢
      • 2018-12-14
      • 2015-08-02
      • 1970-01-01
      • 2012-04-01
      • 2012-06-29
      • 2010-10-07
      • 1970-01-01
      • 2010-12-26
      • 1970-01-01
      相关资源
      最近更新 更多