Stephen RC
Stephen RC

Reputation: 1504

How to check if a function is public or protected in PHP

I am building an API where the user requests a 'command', which is passed into a class. Assuming the command matches a PUBLIC function, it will execute successfully. If the command matches a PROTECTED function, it needs to throw an error.

The idea is that functions can be disabled by changing them from PUBLIC to PROTECTED, rather than renaming them or removing them.

I currently do this, but it doesn't matter if the command is public or protected.

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

Upvotes: 34

Views: 15630

Answers (3)

Master DJon
Master DJon

Reputation: 1965

Though you can not differentiate if the method is private or protected, you can test if public versus not using an external method using is_callable. I made a comparison with "meze" answer.

So:

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')
        ];
    }
}

The method testIfCallable shall be included in a Common class or something similar which you have in your own toolkit because global methods aren't recommended.

I use this in conjunction with the magic methods __get and __set to ensure a public "get/set" method exists.

Tests :

//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>";

Outputs:

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

Final thoughts

If you need to test between all the visibility possibilities, your only way to go is to use testIfCallable2, so the "meze"'s answer. Otherwise, my way is about twice faster. As your question was only between public or not, you could benefit using this. Saying that, if you don't use it often, the difference is not significant.

Upvotes: 4

rhollencamp
rhollencamp

Reputation: 533

You can use the is_callable function to determine if the protection level should limit you: Example:

<?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')));

Upvotes: 12

meze
meze

Reputation: 15097

Simply use 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);
}

Upvotes: 73

Related Questions