2

This is not a duplicate of "In PHP, how do I check if a function exists?".

How do I check if input is callable, either as an existing function or a language construct that behaves like a function?

Community
  • 1
  • 1
Gajus
  • 69,002
  • 70
  • 275
  • 438
  • 1
    Since `empty` is, as you say, *not a function*, it's somewhat vague what you want to know. Why would you be trying to call language constructs using variable code snippets (which is typically not possible to begin with)? – deceze Nov 20 '13 at 14:00
  • 5
    you ask to see if something is a function, but complain aoubt function_exists that it will fail for something that is not a function? – Nanne Nov 20 '13 at 14:00
  • 1
    The intent is to detect whether input callback (whether it is a function or a language construct) can be used to validate user input. – Gajus Nov 20 '13 at 14:01
  • Please describe your real issue. Why do you want this? – Viacheslav Kondratiuk Nov 20 '13 at 14:02
  • `empty` should not be used on variables which you know exist in the first place, so it's a bad callback for validation. `boolval` (PHP 5.5) or simply a test for thruthiness does the same thing. – deceze Nov 20 '13 at 14:03
  • 3
    Guys I too an curious what the use case is for this. Regardless, I think it's a valid question. There are language constructs that _behave_ like functions and then there are actual functions, and it seems valid to ask how to tell if something is callable either way. – jszobody Nov 20 '13 at 14:06
  • The question might be misleading. I suggest rephrasing to "How to check if input is callable construct?", unless someone suggests better title. – Gajus Nov 20 '13 at 14:08
  • @GajusKuizinas There doesn't appear to be a built-in way to do this in PHP. I think it'll require looking at the constructs on http://php.net/manual/en/reserved.keywords.php and manually searching that list along with function_exists(). Example function to follow (since I can't post an answer). – jszobody Nov 20 '13 at 14:08
  • Not sure if it will help you, but you could try to make an array with desired language constructs (like `empty()`), then store them in an array together with the output of `get_defined_functions()`, and check the input with `in_array($needle, $haystack_with_functions)`. – NorthBridge Nov 20 '13 at 14:09
  • 3
    `function canICall($function) { $callableConstructs = array("empty","unset","eval","array","exit","isset","list"); return function_exists($function) || in_array($function, $callableConstructs); }` – jszobody Nov 20 '13 at 14:09
  • @jszobody I've considered this. It seemed *hackish*, in sense that you hard-code the list. Though, I could not find a variable that would list the reserved keywords. Even then, the list would not tell if the keyword can be called as a function. – Gajus Nov 20 '13 at 14:12
  • Yes it's hackish. But without any built-in way to do this in PHP (that I can find) I don't think you have another option. Also, that list is supposed to _only_ the keywords that _can_ be called as functions (it's not complete, just an example). – jszobody Nov 20 '13 at 14:13
  • @jszobody (In response to earlier comment) The problem is that `empty` does *not* behave like a regular function. Its syntax may be function-like, but it actually behaves more like an operator than a function. The behaviour has been made more function-like in PHP 5.5, but it's still not the same. – deceze Nov 20 '13 at 14:13
  • @deceze we all know it's not the same. =) but it can be called like a function, and that's what the OP is looking for. – jszobody Nov 20 '13 at 14:14
  • @jszobody Well no, it *cannot*: `call_user_func('empty', $foo)`. How would you call `list` in this way? – deceze Nov 20 '13 at 14:14
  • @deceze we can go on all day about how it doesn't behave like functions in certain ways. I believe the OP is just looking to test whether you can call "X" like `X('someinput')`. And with `empty()` you can. – jszobody Nov 20 '13 at 14:16
  • @jszobody It seems to me he's trying to do `$validation = 'empty'; if (function_exists($validation)) $validation($value);` Regardless of whether he can make `function_exists` work or not, `$validation($value)` simply does not work with language constructs, so the question seems to be moot. – deceze Nov 20 '13 at 14:18
  • He never said he's trying to use variable function calls. If he is, then I fully agree with you. It's a worthwhile point to make, when providing an answer to his question. Still a valid question. – jszobody Nov 20 '13 at 14:19
  • @jszobody The question doesn't make sense any other way to me. Let's hope he's not looking at `eval`. :) – deceze Nov 20 '13 at 14:20
  • He may very well be looking at eval. In which case you and I both will yell at him and tell him it's almost always a bad idea. =) – jszobody Nov 20 '13 at 14:21
  • 1
    @GajusKuizinas Doesn't look like this question is getting reopened. Suggestion for the future: state your use case. Let folks help you solve your real problem, even if it means approaching it differently. – jszobody Nov 20 '13 at 14:22
  • I was actually looking to call function using `'empty'()` construct. Therefore, this inevitably calls to maintain a list of language constructs *and* their implementation. – Gajus Nov 20 '13 at 14:30

2 Answers2

1

Since empty always exists and is a language construct which cannot be called using variable functions, it doubly makes no sense to want to use function_exists on it. Even if function_exists would work, it is not possible to write this code:

$func = 'empty';
if (function_exists($func)) {
    $func($var);
}

The only way to call empty() is to write it literally in your source code. You cannot call it using variable functions any more than you can do that with list() = or /.

The best you could do is:

if (function_exists('empty')) {
    empty($foo);
}

But because empty always exists, what's the point in testing for it?

If you want to make a validation rule, simply write your own function:

function isEmpty($value) {
    return !$value;
}

$rule = 'isEmpty';

This does exactly the same thing.

Or you make empty a special case in your rule execution:

function validate($rule, $value) {
    switch ($rule) {
        case 'empty' :
            return !$value;

        default :
            if (!function_exists($rule)) {
                throw new InvalidArgumentException("$rule does not exist");
            }
            return $rule($value);
    }
}
deceze
  • 510,633
  • 85
  • 743
  • 889
1

There are all kinds of potential problems with what you're asking. However if I understand you right, you simply want to know if somename can be called like somename('someinput').

If that's true, then it appears you need to use a combination of function_exists and a manual lookup of language constructs from the List of Keywords.

Something like this perhaps:

function canICall($function) {
    $callableConstructs = array("empty","unset","eval","array","exit","isset","list");
    return function_exists($function) || in_array($function, $callableConstructs);
}

That $callableConstructs array is not complete, look at the List of Keywords page to build it out.

Yes it is hackish, but without a built-in way to do this in PHP I don't see another option.

Note that just because you can call something like a function, does not make it a function, nor does it mean that it behaves like a function in other ways.

You cannot call it dynamically:

$var = "empty";
$var('someinput'); // Does NOT work

Nor does this work:

call_user_func('empty', $foo);

You could use it in an eval call, but I hope you understand the huge list of reasons why that can be dangerous.

jszobody
  • 28,495
  • 6
  • 61
  • 72
  • But the calling code would need to look like `if (canICall('empty')) empty($foo)` or `if (canICall($func) && $func == 'empty') empty($foo)`, so... what's the point? – deceze Nov 20 '13 at 14:37
  • 2
    "What's the point?" Again, we're back to his use case. Which we don't know do we? Just trying to answer the simple question... – jszobody Nov 20 '13 at 14:38
  • And IMO the correct answer is to point out that the working of `function_exists` is correct for a reason... :) – deceze Nov 20 '13 at 14:40