用PHP模拟友元类

问题描述 投票:0回答:3

我有一个班级

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
可以通过
Foo
访问
$this->foo
的公共方法和属性。

如何让

FooXDoer
访问
Foo
的私有方法和属性,而不将它们公开给已经使用
Foo
的其余代码?

我应该创建一个单独的类

FooPrivate
,它具有公共的私有方法,并且
Foo
包装,然后让
FooXDoer
引用它吗?
FooPrivate
应该叫什么?

或者我的做法完全错误?你一般如何解决这个问题?

php private friend private-members
3个回答
4
投票

看起来 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;

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


2
投票

PHP 没有友元类的概念,从我读过的内容来看,我不会说这是 php 设计者的错误决定...

恕我直言,没有通用策略,因为问题或问题太宽泛:需要考虑的因素太多:

  • Foo
    需要多少个
    run()
    的私有属性和方法?
  • 从抽象的角度来看:
    run()
    Foo
    的纠缠有多紧密?独立班真的“值得”吗?
  • 你会在
    FooXDoer
    之外使用
    Foo
    吗?

两个解决思路:

将所需的数据从

foo
移交到
fooDoer
,无论是值对值还是通过在
compileRunData()
上实现
Foo
返回数组或对象

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 
{

}

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


0
投票

下面有一个易于使用的函数,可以防止函数被未在“friendClass()”中列出的类使用。在示例中,“Item”对象可能仅由“Foo”类构造。否则,将抛出异常。


    函数 friendClass($classNames){
            如果(!is_array($classNames))
                $classNames = [$classNames];
            $trace = debug_backtrace();
            $called = $trace[1];
            $caller = $trace[2];
            $calledFunction =
                $called['class']."::".$called['function'];
            $callerFunction =
                $caller['class']."::".$caller['function'];
        
            如果(!in_array($caller['class'], $classNames))
                抛出新的异常(
                 “$callerFunction() 未经授权使用 $calledFunction()”
                );
        }
        
        类项目{
            函数 __construct(){
                friendClass('富'); // “好友类”声明
                echo "Item 对象已创建
";
            }
        };
        
        类 Foo{
            函数调用者(){
                $项目=新项目();
            }
        };
        
        酒吧类{
            函数调用者(){
                $项目=新项目();
            }
        };
        
        $foo = new Foo();
        $酒吧=新酒吧();
        $foo->调用者(); //允许
        $项目=新项目(); // 例外
        $bar->caller(); //例外

© www.soinside.com 2019 - 2024. All rights reserved.