PHP获取可见性

4

在php中,是否有可能获取类内部方法和属性的可见性?

我想要做到类似这样的操作:

function __call($method, $args)
{
    if(is_callable(array($this,$method))
    {
        if(get_visibility(array($this,$method)) == 'private')
            //dosomething
        elseif(get_visibility(array($this,$method)) == 'protected')
            //dosomething
        else
            //dosomething
    } 
} 

1
如果使用反射,这可能是可行的,但我无法保证性能。此外,我想不出任何情况下需要能够这样做。也许你最好考虑一下为什么想要这样做,以及是否真的有必要。你不想用不必要的复杂性和魔法来混淆你的代码。 - GordonM
知道可见性的一个用例是,如果您尝试在实现魔术方法__get()时模拟内置PHP错误的精度。 - faintsignal
3个回答

7

is_callable考虑到可见性,但由于您是从类内部使用它,因此它将始终计算为TRUE

要获取方法的可见性,您需要使用反射API并检查方法的修饰符

来自PHP手册的简化示例:

class Testing
{
    final public static function foo()
    {
        return;
    }
}

// this would go into your __call method
$foo = new ReflectionMethod('Testing', 'foo');
echo implode(
    Reflection::getModifierNames(
        $foo->getModifiers()
    )
); // outputs finalpublicstatic

同样适用于属性

然而,由于反射类的复杂性,这可能会很慢。您应该进行基准测试以查看它是否对您的应用程序产生太大影响。


我刚刚进行了一个快速的基准测试,似乎检查方法是否具有某种可见性然后调用该方法比直接调用该方法慢大约3倍。对于较大的方法,这可能是可以忽略的,但对于较小的方法(或属性)则不是。 - Tiddo
@Tiddo请注意,首先使用__call__get已经相当慢了。因此,除非您的基准测试列出了单个函数调用,否则您对结果的解释可能是错误的。另外,您最好通过接口和组合/聚合以及策略和装饰器来解决多重继承问题。 - Gordon
我知道,但我在没有使用__call或__get方法的情况下进行了测试:我从类外部多次调用了一个函数,有时检查修饰符,有时不检查。我知道最好通过接口等方式解决这些问题,但在某些情况下,使用多重继承解决这些问题会更容易。 - Tiddo

6
你可能需要考虑使用PHP的反射API来解决这个问题。然而,我也应该问一下你,为什么要这样做,因为反射通常只在开始时有点hacky的情况下使用。虽然这是可能的,所以接下来就是解释:
<?php

class Foo {
    /**
     *
     * @var ReflectionClass
     */
    protected $reflection;
    protected function bar( ) {

    }

    private function baz( ) {

    }

    public function __call( $method, $args ) {
        if( ( $reflMethod = $this->method( $method ) ) !== false ) {
            if( $reflMethod->isPrivate( ) ) {
                echo "That's private.<br />\n";
            }
            elseif( $reflMethod->isProtected( ) ) {
                echo "That's protected.<br />\n";
            }
        }
    }

    protected function method( $name ) {
        if( !isset( $this->methods[$name] ) ) {
            if( $this->reflect( )->hasMethod( $name ) ) {
                $this->methods[$name] = $this->reflect( )->getMethod( $name );
            }
            else {
                $this->methods[$name] = false;
            }
        }
        return $this->methods[$name];
    }

    protected function reflect( ) {
        if( !isset( $this->reflection ) ) {
            $this->reflection = new ReflectionClass( $this );
        }
        return $this->reflection;
    }
}

$foo = new Foo( );
$foo->baz( );
$foo->bar( );

我想这样做是因为我只是在尝试在php中模拟多重继承。但是要使其完全功能,我需要能够区分私有和受保护的方法。离题一点:真巧,我们都来自荷兰,并且有相同的姓氏。 - Tiddo
哈哈,真是个巧合。你有没有阅读关于PHP中水平复用的RFC呢?这已经提交到主干一段时间了。如果还没有看过,请在这里查看;http://wiki.php.net/rfc/traits - Berry Langerak
我已经阅读了它。我希望它能在 PHP 的下一个版本或者之后的版本中出现,但是我必须说,对我来说,这似乎只是针对方法的多重继承,所以我真的不明白为什么这比 mi 好得多。(例如,你仍然需要手动解决菱形继承问题) - Tiddo
2
尽管您认为发帖者的做法可能不太好,但还是回答了问题,这点值得赞赏。 - Omnimike
@tiddo 多重继承是一个不好的特性,很少有面向对象语言支持它的原因是有充分的理由的。你可以使用 PHP traits 和接口来实现类似的功能,但我从未必须这样做,并且我认为 traits 也是一个不好的特性。 - GordonM

-1

这个回答有点晚,但我认为通过提到get_class_methods()method_exists()的组合仍然可以增加一些价值:

<?php
class Foo {
    // ...
    public function getVisibility($method) {
        if ( method_exists($this, $method) && in_array($method, get_class_methods($this)) ) {
            return 'protected or public';
        } else {
            return 'private';
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接