在PHP中模拟友元类

iugsix8n  于 2023-02-28  发布在  PHP
关注(0)|答案(3)|浏览(85)

我有一个类Foo,它有很多公共方法和私有方法,其中一个方法变得相当大,我想把它分成一个单独的类专门用于这个目的,类似于:

<?php

class Foo
{
  //  ...

  public function doX( $a, $b )
  {
    $x = new FooXDoer;
    $x->foo = $this;
    return $x->run( $a, $b );
  }

  //  ...
}

class FooXDoer
{
  public $foo;

  public function run( $a, $b )
  {
    // ...
  }

  // ...
}

FooXDoer可以通过$this->foo访问Foo的公共方法和属性。
我怎样才能给予FooXDoer访问Foo的私有方法和属性,而不把它们对已经使用Foo的其他代码公开呢?
我是否应该创建一个单独的类FooPrivate,它将private方法作为public方法,并 Package Foo,然后让FooXDoer引用它?FooPrivate应该调用什么?
或者我的方法完全错了?一般来说你是怎么解决这个问题的?

nkhmeac6

nkhmeac61#

看起来traits在使用PHP〉= 5.4的情况下能最好地解决您的问题。
如果没有,我想到了以下解决方案:

class A {
    private static $allowedClasses = array('B');
    private $a = 1;

    public function __get($property) {
        $caller = debug_backtrace(false);
        if(!isset($caller[1]))
            throw new Exception('Bla bla');
        if(!in_array($caller[1]['class'], self::$allowedClasses))
            throw new Exception('Bla bla');
        return $this->$property;
    }

    public function testB() {
        $b = new B();
        $b->instA = $this;
        echo $b->getA();
    }

}

class B {
    public $instA;
    public function getA() {
        return $this->instA->a;
    }
}

class C {
    public function getA() {
        $instA = new A();
        return $instA->a;
    }
}

$a = new A();
$a->testB(); // Works ok;
$c = new C();
$c->getA(); // Throws exception here;

这段代码绝对不是最佳实践:)但既然有可能,我就把它放在这里。

b4lqfgs4

b4lqfgs42#

PHP没有朋友类的概念,从我所读到的我不会说这是一个错误的决定,由php设计师...
恕我直言,没有一般的战略,因为问题太广泛:要考虑的因素太多了:

  • run()中需要多少Foo的私有属性和方法?
  • 从一个抽象的Angular 来看:run()Foo纠缠得有多紧密?它真的“配”在一个独立的类中吗?
  • 你会在Foo之外使用FooXDoer吗?
    两个解决方案:

将所需的数据从foo移交到fooDoer,可以是值对值,也可以是在Foo上实现compileRunData()(返回数组或对象)

public function doX( $a, $b )
{
 $x = new FooXDoer;
 $workEnvironment = $this->compileRunData();
 $x->setEnvironment( $workEnvironment );
 $x->foo = $this;
 return $x->run( $a, $b );
}

或者使用继承,特别是保护属性的概念:

abstract class FooAbstract
{
 protected $_basicVar1;
 protected function basicMethod1(/*...*/) {
  //...
 }
 // ...
}

abstract class FooRunner extends FooAbstract
{
 protected $_runVar1;

 protected function runMethod1(/*...*/) {
  //...
 }

 public function run($a, $b) {
  // ...
 }
}

public class Domain_Model_Foo extends FooRunner 
{

}

编辑:嘿,所以我没有告诉我已经有答案了。是的,也想过特性,但直到现在还没有使用它们,所以不能真正评论它们

7kqas0il

7kqas0il3#

下面是一个简单易用的函数,可以防止函数被未在“friendClass()"中列出的类使用,在这个例子中,“Item”对象只能由“Foo”类构造,否则抛出异常。

function friendClass($classNames){
            if(!is_array($classNames))
                $classNames = [$classNames];
            $trace = debug_backtrace();
            $called = $trace[1];
            $caller = $trace[2];
            $calledFunction =
                $called['class']."::".$called['function'];
            $callerFunction =
                $caller['class']."::".$caller['function'];
        
            if(!in_array($caller['class'], $classNames))
                throw new exception( 
                 "Unauthorized use of $calledFunction() by  $callerFunction()"
                );
        }
        
        class Item{
            function __construct(){
                friendClass('Foo'); // "Friend class" declaration
                echo "Item object created\n";
            }
        };
        
        class Foo{
            function caller(){
                $item = new Item();
            }
        };
        
        class Bar{
            function caller(){
                $item = new Item();
            }
        };
        
        $foo = new Foo();
        $bar = new Bar();
        $foo->caller(); //allowed
        $item = new Item(); // exception
        $bar->caller(); //exception

相关问题