【问题标题】:Reference a method of container object in PHP?在 PHP 中引用容器对象的方法?
【发布时间】:2009-08-06 17:30:46
【问题描述】:

在 PHP 中给出以下内容:

<?php
class foo {
  public $bar;
  function __construct() {
    "Foo Exists!";
  }

  function magic_bullet($id) {
    switch($id) {
    case 1:
      echo "There is no spoon! ";
    case 2:
      echo "Or is there... ";
      break;
    }
  }
}

class bar {
  function __construct() {
    echo "Bar exists";
  }
  function target($id) {
    echo "I want a magic bullet for this ID!";
  }
}

$test = new foo();
$test->bar = new bar();
$test->bar->target(42);

我想知道'bar' 类是否可以调用'foo' 类的'magic bullet' 方法。 'bar' 实例包含在 'foo' 实例中,但与它没有父/子关系。实际上,我有许多不同的“bar”类,“foo”在一个数组中,每个都做了一些与 $id 不同的事情,然后想要将它传递给“magic_bullet”函数以获得最终结果,所以禁止一个结构类关系的变化,是否可以访问“容器”实例的方法?

【问题讨论】:

    标签: php


    【解决方案1】:

    您必须修改代码以提供关系。在 OOP 中,我们称之为aggregation

    假设 PHP 4,以及“条形数组”的想法

    <?php
    
    class foo {
      var $bars = array();
      function __construct() {
        "Foo Exists!";
      }
    
      function magic_bullet($id) {
        switch($id) {
        case 1:
          echo "There is no spoon! ";
        case 2:
          echo "Or is there... ";
          break;
        }
      }
    
      function addBar( &$bar )
      {
        $bar->setFoo( $this );
        $this->bars[] = &$bar;
      }
    }
    
    class bar {
      var $foo;
      function __construct() {
        echo "Bar exists";
      }
    
      function target($id){
        if ( isset( $this->foo ) )
        {
          echo $this->foo->magic_bullet( $id );
        } else {
          trigger_error( 'There is no foo!', E_USER_ERROR );
        }
      }
      function setFoo( &$foo )
      {
        $this->foo = &$foo;
      }
    }
    
    $test = new foo();
    $bar1 = new bar();
    $bar2 = new bar();
    
    $test->addBar( $bar1 );
    $test->addBar( $bar2 );
    
    $bar1->target( 1 );
    $bar1->target( 2 );
    

    【讨论】:

    • 太棒了!这适用于我的设置,高于为 bar 实例添加指向 foo 实例的指针的其他建议,因为假设有许多“bar”实例,我不希望 foo 实例在 bar 实例中的副本(额外的内存使用,加上如果原始的 $test 实例更改了修改 magic_bullet 行为的某些内容,则所有副本都会过时),我想要一个参考($this-&gt;pointer_to_foo = &amp;$foo;,而不是 $this-&gt;pointer_to_foo = $foo)。
    • 考虑到您正在使用 PHP5(如果使用 __construct 作为构造函数),应该不需要 & 符号:对象默认通过引用传递
    • 哈哈,我不知道我是怎么错过 PHP 5 风格的构造函数的,我只是关闭了 var 的使用以及方法上缺少访问/可见性修饰符。 @MidnightLightning 如果您实际上使用的是 PHP 5,Pascal MARTIN 是正确的 - 您不需要参考运算符。对于我造成的任何混乱,我们深表歉意。
    • @MidnightLightning - 在 PHP 5 中你永远不会得到一个对象的副本,除非你特别使用 clone 操作,因为对象总是通过引用传递。如果您有__construct() 函数,那么您肯定会使用PHP5,因此您无需担心使用&amp; 引用运算符。事实上,通常不鼓励这样做。
    • @zombat, et.al.感谢您指出对象不需要“&”!
    【解决方案2】:

    不,这是不可能的,因为没有定义关系。

    我建议不要直接设置$test-&gt;bar 属性,而是使用setter,这样就可以建立关系。您还需要将container 属性添加到bar 类。课堂内foo

    function setBar(bar $bar)
    {
        $bar->container = $this;
        $this->bar = $bar;
    }
    

    这建立了对象之间的关系。现在将bar::target($id) 函数更改为:

    function target($id)
    {
        $this->container->magic_bullet($id);
    }
    

    你现在应该可以做到了:

    $test = new foo();
    $bar = new bar();
    $test->setBar($bar);
    $test->bar->target(42);
    

    【讨论】:

      【解决方案3】:

      这在我看来(尽管仅基于您展示的代码!)是静态方法的完美案例...

      <?php
      class foo {
        var $bar;
        function __construct() {
          "Foo Exists!";
        }
      
        static function magic_bullet($id) {
          switch($id) {
          case 1:
            echo "There is no spoon! ";
          case 2:
            echo "Or is there... ";
            break;
          }
        }
      }
      
      class bar {
        function __construct() {
          echo "Bar exists";
        }
        function target($id) {
          echo Foo::magic_bullet($id)
        }
      }
      
      $test = new foo();
      $test->bar = new bar();
      $test->bar->target(42);
      

      或者,如果只有一个“Foo”对象——也许是单例设计模式?

      【讨论】:

      • 太棒了!是的,根据我提供的内容,使用静态方法或 Singlton 将解决让每个人都可以看到 magic_bullet 的问题。但实际上,我希望 'bar' 能够访问 'foo' 的不止一个函数,而不是让它们都成为静态的,我将使用来自另一个答案的指针解决方案
      • 如果里面的东西不需要访问栏...那么单身可能是最好的主意。然后你会做类似 Foo::getInstance()->any_of_your_functions($vars);
      【解决方案4】:

      $bar 实例,在 Foo 内部,无法知道包含它的类是什么;因此,无法调用该方法的任何方法。

      (好吧,也许(可能不)使用反射......我应该尝试一下,有一天^^)

      编辑:考虑一下:您可以在 bar 类中保留指向 foo 的指针;有点像这样:

      class foo {
        var $bar;
        function __construct() {
          var_dump("Foo Exists!");
        }
      
        function magic_bullet($id) {
            var_dump('magic_bullet : ' . $id);
          switch($id) {
          case 1:
            var_dump("There is no spoon! ");
          case 2:
            var_dump("Or is there... ");
            break;
          }
        }
      }
      
      class bar {
        protected $pointer_to_foo;
        function __construct($pointer_to_foo) {
          var_dump("Bar exists");
          $this->pointer_to_foo = $pointer_to_foo;
        }
        function target($id) {
          var_dump("I want a magic bullet for this ID!");
          if (method_exists($this->pointer_to_foo, 'magic_bullet')) {
              $this->pointer_to_foo->magic_bullet($id);
          }
        }
      }
      
      $test = new foo();
      $test->bar = new bar($test);
      $test->bar->target(42);
      

      请注意,我使用method_exists 作为安全措施。

      你会得到:

      string 'Foo Exists!' (length=11)
      
      string 'Bar exists' (length=10)
      
      string 'I want a magic bullet for this ID!' (length=34)
      
      string 'magic_bullet : 42' (length=17)
      

      所以,可以做到...如果您稍微修改一下代码;-)


      编辑 2:嗬,zombat 打败了我,看来 :-(

      【讨论】:

        【解决方案5】:

        这里有几个选项。我建议不要走全球路线。

        $test->bar = new bar($test);

        如果您随后将 $test 存储在 bar 的构造函数中,这将起作用。您还可以:

        类测试{ ... 公共功能目标($someInt){ $this->bar->target($someInt,$this); } } $测试->目标(42);

        ...并在 bar 的 target() 方法中使用给定的测试对象。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-12-21
          • 1970-01-01
          • 1970-01-01
          • 2020-01-19
          • 2020-09-15
          • 1970-01-01
          • 1970-01-01
          • 2012-05-29
          相关资源
          最近更新 更多