访问非公开成员

时间:2013-12-10 21:32:57

标签: php getter-setter

我正在创建域对象。我希望这些对象具有将用于检查要分配给每个属性的值的有效性的setter,但我还希望能够设置此属性而不执行检查是否假定数据正确。

我可以做以下事情:

class DO_Group
{
    protected $_name = null;

    public function getName()
    {
        return $this->_name;
    }

    public function setName( $name )
    {
        $length = strlen( $name );

        if( 4 <= $length && $length <= 16 && preg_match( '^[A-Z]*(_[A-Z]*)*$', $name ) == 1 )
            $this->_name = $name;
        else
            throw new Exception( '::DO_Group::setName() - Invalid name.' );
    }
}

但是如果传递给setName()的$ name已知有效,我将不得不调用setName,从而执行不需要的检查,以设置该属性。

我可以使用反射API或调试跟踪黑客,但我想不使用这两种方法,因为它们在多次使用时性能不佳(并且很脏,IMO)。

我考虑过添加一个不检查值的方法:

public function setNameNoCheck( $name )
{
    $this->_name = $name;
}

但在这种情况下,使财产不公开是没有意义的。如果我选择公开,代码用户可能会忘记在需要时检查它。

我可以这样做:

abstract class BaseClass
{
    public function setProperty( $name, $value )
    {
        $this->$name = $value;
    }
}

让我的域对象扩展这个类?引用未声明的属性是正确的PHP吗? (因为它们不存在于基类中)

3 个答案:

答案 0 :(得分:1)

我想我的建议是只使用你现在拥有的两种情况的方法:

public function setName( $name )
{
        $length = strlen( $name );

        if( 4 <= $length && $length <= 16 && preg_match( '^[A-Z]*(_[A-Z]*)*$', $name ) == 1 )
            $this->_name = $name;
        else
            throw new Exception( '::DO_Group::setName() - Invalid name.' );
}

在这种情况下,您可以确定您的数据库将始终保持其完整性。即使有一些情况,你是积极的,价值是有效的,开销将是最小的。它似乎是一个可以保证工作的干净解决方案。

编辑:

如果对象已存在于数据库中,为什么不创建一个类方法来处理它?当你直接从数据库中提取值时调用它。

public function populateFields( $row )
{
    $this->_name = $row['name'];
    // ... populate other fields here
}

这将立即填充所有字段,而不必为每个属性编写和调用单个函数。

答案 1 :(得分:1)

如果你使用PHP 5.4+,那么这里有一个很好的代码片段。几个月前我想出了这个。如果您使用的是PHP 5.3或更低版本,您仍然可以使用call_user_func使其工作。然而,在匿名函数的帮助下,它更干净,更冗长。

想法是制作一种friend对象。类似于C ++中的friend类,但是对特定对象而不是类进行操作。

让我们开始吧。首先,我们需要准备一下你的课程:

class DoGroup
{
    protected $_name = null;

    public function makeFriend(IDoGroupFriend $newFriend)
    {
        $newFriend->plugNameSetter(function($name){
            $this->_doSetName($name);
        });
    }

    public function setName( $name )
    {
        // do the costly checking here...
        /// and set the variable:
        $this->_doSetName($name);
    }

    protected function _doSetName($name)
    {
        $this->_name = $name;
    }
}

接下来,我们将定义该朋友界面:

interface IDoGroupFriend
{
    public function plugNameSetter(callable $nameSetterFn);
}

现在我们可以创建朋友了:

class ExampleFriend implements IDoGroupFriend
{
    private $_nameSetterFn = null;

    public function plugNameSetter(callable $nameSetterFn)
    {
        $this->_nameSetterFn = $nameSetterFn;
    }

    public function setTheNameFromFriend($name)
    {
        $nameSetterFn = $this->_nameSetterFn;
        $nameSetterFn($name);
    }
}

最后,我们可以按照以下方式使用朋友:

$Main = new DoGroup();
$Friend = new ExampleFriend();

$Main->makeFriend($Friend);

// tadam!
$Friend->setTheNameViaFriend('name via friend');

当然,你真正的实施可能会更加复杂,然后盲目地传递这个名字。如果$name(在这种情况下为ExampleFriend对象)的来源是可信的,那么我假设它来自不同的可信来源。在这种情况下,您将以不同的方式实现setTheNameFromFriend - 例如,作为数据库提取方法。

此外,您可能不希望公开DoGroup::makeFriend()方法。我通常只从DoGroup类的构造函数调用此方法,所以我喜欢将它私有化。

答案 2 :(得分:0)

创建安全模式($ this-&gt; safeMode = false)和

       public function setName( $name )
    {
if ($this->safeMode===true){ $this->_name = $name; return;}

            $length = strlen( $name );

            if( 4 <= $length && $length <= 16 && preg_match( '^[A-Z]*(_[A-Z]*)*$', $name ) == 1 )
                $this->_name = $name;
            else
                throw new Exception( '::DO_Group::setName() - Invalid name.' );
    }
如果数据来自数据库或其他&#34; safe&#34;

将safeMode设置为true。 source(或类扩展数据库类)