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

Я строю API, где пользователь запрашивает "команду", которая передается в класс. Предполагая, что команда соответствует функции PUBLIC, она будет выполнена успешно. Если команда соответствует ЗАЩИЩЕННОЙ функции, она должна выдать ошибку.

Идея заключается в том, что функции можно отключить, изменив их с ОБЩЕГО на ЗАЩИЩЕННЫЕ, а не переименовывая или удаляя их.

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

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

3 ответа

Решение

Просто используйте 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);
}

Вы можете использовать функцию 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')));

Хотя вы не можете различить, является ли метод закрытым или защищенным, вы можете проверить, является ли метод общедоступным, а не использовать внешний метод, используя 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 должны быть включены в класс Common или что-то подобное, что есть в вашем собственном наборе инструментов, потому что глобальные методы не рекомендуются.

Я использую это в сочетании с магическими методами __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так что ответ "мезе". В противном случае мой путь примерно в два раза быстрее. Поскольку ваш вопрос был только между публикой или нет, вы могли бы извлечь выгоду, используя это. Сказать, что, если вы не используете его часто, разница не значительна.

Другие вопросы по тегам