【问题标题】:PHP isset($this) and using the same object method in a static and object contextPHP isset($this) 并在静态和对象上下文中使用相同的对象方法
【发布时间】:2011-01-21 19:02:10
【问题描述】:

我正在研究一个需要通过静态函数调用和对象方法访问的类。我发现的一件事是我正在跨多个函数复制逻辑。

简化示例:

class Configurable{

    protected $configurations = array();

    protected static $static_configurations = array();

    public function configure($name, $value){

        // ...lots of validation logic...

        $this->configurations[$name] = $value;

        }

     public static function static_configure($name, $value){

        // ...lots of validation logic (repeated)...

        self::$static_configurations[$name] = $value;

        }

    }

我找到了解决办法,但感觉真的很脏:

class Configurable{

    protected $configurations = array();

    protected static $static_configurations = array();

    public function configure($name, $value){

        // ...lots of validation logic...

        if (isset($this)){
            $this->configurations[$name] = $value;
            }
        else{
            self::$static_configurations[$name] = $value;
            }

        }

    }

我还需要静态函数,以便我可以在整个应用程序中设置配置。此外,这种技术的好处是我可以在两个范围内使用相同的方法名称。

这样的测试范围有什么问题吗?性能问题、前向兼容性问题等。在 PHP 5.2 上都对我有用,我不需要支持

【问题讨论】:

    标签: php oop static scope isset


    【解决方案1】:

    在核心 php 中我们使用index.php?var=,所以在 oop php 中做同样的事情我们应该使用什么。

    【讨论】:

      【解决方案2】:

      我不觉得允许在实例上调用方法以及静态调用方法很荒谬。我的情况:

         TestRecord::generateForm(); // Generate an empty form.
      
         $test = new TestRecord( $primaryKey );
         [...]
         $test->generateForm();      // Generate an edit form with actual $test values.
      

      我班级的静态部分处理空白/新逻辑, 而实例端意味着使用实时数据。

      PHP 5.3 允许通过使用__call__callStaticstatic:: 来实现这一点:

      public function __call( $name, $args )
      {
        if ( $name == 'generateForm' ) {
          $this->fields = static::createFields();  // Action 1 : static.
          $this->fillFields();                     // Action 2 : instance.
          static::renderForm( $this->fields );     // Action 3 : static.
        }
      }
      
      public static function __callStatic( $name, $args )
      {
        if ( $name == 'generateForm' ) {
          $fields = static::createFields();        // Action 1 : static.
                                                   // Action 2 : none.
          static::renderForm( $fields );           // Action 3 : static.
        }
      }
      

      注意:使用 static:: 后期绑定限定符是因为我的 3 个操作方法(createFieldsfillFieldsrendreForm)在 this 的子类中实现为 protected一,即abstract。这是可能的,因为 PHP 允许从两个方向访问受保护的成员:从基类到子类,但也从子类到超类。据我所知,这与其他 OO 语言不同。

      【讨论】:

        【解决方案3】:

        静态方法需要的参数数量与其对应的目标方法不同——额外的参数将是一个执行上下文。如果没有执行上下文,那么只有静态调用它才有意义。

        鉴于我正在构建一个具有多个这样的接口的库,我的首选方法是创建一个静态类和一个动态类。让一个代理呼叫另一个。例如:

        class DynamicClass {
            protected $foo;
            protected $bar;
            public function baz($arg1) {
                return StaticClass::bar($this->foo, $arg1);
            }
            public function zop($arg1, $arg2) {
                return StaticClass::zop($this->foo, $this->bar, $arg1, $arg2);
            }
            // Context-less helper function
            public function womp($arg1) {
                return StaticClass::womp($arg1);
            }
        }
        
        class StaticClass {
            public static function baz(&$fooContext, $arg1) { ... }
            public static function zop(&$fooContext, &$barContext, $arg1, $arg2) { ... }
            public static function womp($arg1) { ... }
        }
        

        如何将上下文传递给静态类完全取决于您 - 您必须做任何对您有意义的事情。在大多数函数中完成的工作应该很小(如果你做了很多工作,那么你可能应该将工作分解成更小的函数),因此只需要少数上下文参数。或者,您可以创建一个完整的上下文数组并将其传递到各处(在每次调用之前将其填充到 DynamicClass 中,或者跟踪该数组中的所有 DynamicClass 属性,以便您可以快速轻松地传递它。


        虽然实际上看起来您可能会从单例设计模式中受益。据我所知,您正在尝试创建一个全局Configurable,并且还可以选择创建单个本地Configurables。使用单例设计模式,您可以创建一个类的全局可访问版本,您可以保证您只有一个(不违反 OOP 设计原则并且不必依赖 $_GLOBALS 等)。例如:

        class DynamicClass {
            protected $foo;
            protected $bar;
        
            public function baz($arg1) { ... }
            public function zop($arg1, $arg2) { ... }
        
            public static function getSingleton() {
                static $instance = null;
                if ($instance === null) $instance = new DynamicClass();
                return $instance;
            }
        }
        

        无论您在代码中的哪个位置,都可以使用DynamicClass::getSingleton() 访问同一个实例。您还可以选择创建一次性的非单例版本。您基本上可以两全其美,同时只需要专门编写具有动态访问权限的所有方法。

        【讨论】:

          【解决方案4】:

          第二种方法的问题是当错误报告设置为E_STRICT时会导致错误。例如:

          严格标准:非静态方法 Foo::bar() 不应在 /home/yacoby/dev/php/test.php 第 10 行静态调用

          PHP6 的一个特点是将 E_STRICT 错误移至 E_ALL。换句话说,E_ALL 将涵盖所有错误,包括不允许您静态调用非静态方法。

          另一种方法可能是将验证逻辑移至静态函数。这样非静态函数和静态函数都可以调用验证逻辑。

          【讨论】:

          • 好的,除了严格的错误,第二种方法有什么问题吗?无论如何,我可能会根据您的建议更改它,但我想了解使用这样的代码的真正问题。它是可能从未来的 PHP 版本中删除的功能,还是您始终能够静态调用非静态方法?
          • E_STRICT 错误往往是“这似乎是一个非常糟糕的主意,但无论如何我们都会让你尝试这样做。”不能保证这样的东西会一直有效,但它们可能会在完全弃用之前转换为 E_NOTICEE_WARN
          • 感谢您澄清@MightyE
          • @Yacoby 如何在静态 fn 中进行验证?必须不使用isset($this),因为$this 永远无法从静态fn 中获得——那你还能怎么做呢?
          猜你喜欢
          • 2016-10-03
          • 2013-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-02-12
          • 1970-01-01
          • 1970-01-01
          • 2023-03-22
          相关资源
          最近更新 更多