Как проверить, является ли функция общедоступной или защищенной в PHP

Я создаю API, где пользователь запрашивает "команду", которая передается в класс. Предполагая, что команда соответствует функции PUBLIC, она будет выполнена успешно. Если команда соответствует функции PROTECTED, ей необходимо выдать ошибку.

Идея состоит в том, что функции можно отключить, изменив их с PUBLIC на PROTECTED, вместо того, чтобы переименовывать их или удалять.

В настоящее время я делаю это, но неважно, является ли команда общедоступной или защищенной.

<?php
/**
 * Look for Command method
 */
$sMethod = "{$sCommand}Command";
if (method_exists($this, $sMethod))
{
    /**
     * Run the command
     */
    return $this->$sMethod($aParameters);
}

Ответ 1

Просто используйте ReflectionMethod:

/**
 * Look for Command method
 */
if (method_exists($this, $sMethod))
{
    $reflection = new ReflectionMethod($this, $sMethod);
    if (!$reflection->isPublic()) {
        throw new RuntimeException("The called method is not public.");
    }
    /**
     * Run the command
     */
    return $this->$sMethod($aParameters);
}

Ответ 2

Вы можете использовать функцию is_callable, чтобы определить, должен ли уровень защиты ограничивать вас: Пример:

<?php
class FooBar {
    protected function Foo() { return; }
    public function Bar() { return; }
}

$foo = new FooBar();

var_dump(is_callable(array($foo, 'Foo')));
var_dump(is_callable(array($foo, 'Bar')));

Ответ 3

Хотя вы не можете отличить, является ли метод приватным или защищенным, вы можете проверить, существует ли public или нет внешнего метода, используя is_callable. Я сделал сравнение с ответом "Мезе".

Итак:

function testIfCallable($object, $method) {
    return is_callable(array($object, $method));
}

function testIfCallable2($object, $method) {
    if (method_exists($object, $method))
    {
        $reflection = new ReflectionMethod($object, $method);
        return $reflection->isPublic();
    }

    return false;
}

class Test {

    private function privateMethod() {

    }

    protected function protectedMethod() {

    }

    public function publicMethod() {

    }

    public function testAccessibility() {
        if (testIfCallable($this, 'privateMethod')) echo "YYY<br>"; else echo 'NNN<br>';
        if (testIfCallable($this, 'protectedMethod')) echo "YYY<br>"; else echo 'NNN<br>';
        if (testIfCallable($this, 'publicMethod')) echo "YYY<br>"; else echo 'NNN<br>';
    }

    public function testAccessibility2() {
        if (testIfCallable2($this, 'privateMethod')) echo "YYY<br>"; else echo 'NNN<br>';
        if (testIfCallable2($this, 'protectedMethod')) echo "YYY<br>"; else echo 'NNN<br>';
        if (testIfCallable2($this, 'publicMethod')) echo "YYY<br>"; else echo 'NNN<br>';
    }       

    public function testSpeedAccessibility() {
        return $results = [
                testIfCallable($this, 'privateMethod'),
                testIfCallable($this, 'protectedMethod'),
                testIfCallable($this, 'publicMethod')
        ];
    }

    public function testSpeedAccesibility2() {
        return $results = [
                testIfCallable2($this, 'privateMethod'),
                testIfCallable2($this, 'protectedMethod'),
                testIfCallable2($this, 'publicMethod')
        ];
    }
}

Метод testIfCallable должен быть включен в общий класс или что-то подобное, что у вас есть в вашем собственном наборе инструментов, поскольку глобальные методы не рекомендуются.

Я использую это в сочетании с магическими методами __get и __set, чтобы обеспечить общедоступный метод get/set.

Тесты:

//Test functionality
$t = new Test();
$t->testAccessibility();
$t->testAccessibility2();

//Test speed
$start = microtime(true);
for($i = 0; $i < 10000; $i++) {
    $t->testSpeedAccessibility();
}
echo "Is Callable way: " . (microtime(true) - $start) . "ms<br>";

$start = microtime(true);
for($i = 0; $i < 10000; $i++) {
    $t->testSpeedAccesibility2();
}
echo "Reflection way: " . (microtime(true) - $start) . "ms<br>";

Выходы:

NNN
NNN
YYY
NNN
NNN
YYY
Is Callable way: 0.23506498336792ms
Reflection way: 0.45829010009766ms

Заключительные мысли

Если вам нужно протестировать все возможности видимости, вы можете использовать только testIfCallable2, поэтому ответ "meze". В противном случае мой путь примерно в два раза быстрее. Поскольку ваш вопрос был только между публикой или нет, вы могли бы воспользоваться этим. Говоря, что, если вы не используете его часто, разница не имеет значения.