
Dynamic Invocation in PHP Exposed - KrisJordan
http://www.recessframework.org/page/php-callables-is-callable-call-user-func-array-reflection
======
gdp
Sigh. Must we? This kind of large-scale dynamic behaviour tends to go wrong
even in languages that provide type-checking and sensible lexical scoping. In
PHP it's basically just a recipe for the kinds of untouchable "black magic"
code that haunt code bases. It's basically some nasty syntax wrapped around
even nastier GOTO-style behaviour.

"is_callable" is no better, because if you don't know, you shouldn't even be
trying it. It might as well be called "does_stick", because you're basically
just throwing spaghetti (code) at a wall.

~~~
KrisJordan
Having a blog about programming languages and formal methods I understand the
basis of your disgust with PHP. In a perfect world all programming languages
would use robust type systems and be capable of formal verification. PHP makes
no attempt at either.

Out in user land there are all sorts of scenarios where dynamic invocation
makes sense. Higher-order functions are the most obvious. Is mapping
strtolower on an array of strings really spaghetti code?

Most uses for dynamic invocation are at the utility/framework level, not in
application land. You're right, it's dangerous when used improperly, but you
should consider coming down from the theory clouds if you believe dynamic
invocation and goto are in the same boat on the river styx.

~~~
gdp
> _Having a blog about programming languages and formal methods I understand
> the basis of your disgust with PHP. In a perfect world all programming
> languages would use robust type systems and be capable of formal
> verification. PHP makes no attempt at either._

Way to straw-man, dude! Nowhere did I suggest anything about perfect worlds or
what programming languages should be like. I said that this particular feature
of this particular language is undesirable because it introduces unnecessary
complexity and risky levels of unpredictability into code that probably
doesn't need to use it.

> _Out in user land there are all sorts of scenarios where dynamic invocation
> makes sense. Higher-order functions are the most obvious. Is mapping
> strtolower on an array of strings really spaghetti code?_

What does "dynamic invocation" have to do with higher-order functions? Pretty
much everything described in the article deals only with functions as first-
class values and unrestricted indirection. There is nothing "higher order"
about them. If you're talking about _implementing_ higher-order functions,
then the only requirement is the ability to pass around functions as values.
There is certainly no requirement for indirection on strings!

If functions are first-class values, that's absolutely fine. I'm complaining
about situations where you end up passing _names of functions_ around (as
strings), and they take on special meanings along the way. I characterise this
as spaghetti code because it leads to a situation whereby control flow is
embedded both within constructs in the language, _and_ within computed values
based on using indirection on strings-indexing-functions. That's just nasty.

Just so I'm not misinterpreted, let me furnish my complaint with an example to
show what I am and am not complaining about:

    
    
      ...
      $x = "hello$z$k$y";
      $x($foobar);
      

vs.

    
    
      ...
      $x = function() { echo "foo"; };
      $x();
    

The latter is (mostly) fine. The former is completely bonkers, and is what I
am complaining about - the ability to do ad hoc indirection based on
calculated values. In the first case, functions are _not_ values, and
_strings_ have become semantically significant. In the latter case, the
function _is_ a first-class value, and there is absolutely no magic going on -
everything is hunky dory.

> _Most uses for dynamic invocation are at the utility/framework level, not in
> application land._

I still can only think of a few restricted cases where indirection based upon
strings (that values of which are not known until runtime) would be sensible
or useful. The kinds of cases I can think of are where you would want to do
something really dubious based on user-input. Keep in mind that I'm not
railing against _reflection_ here! Any sensible dynamic language should be
able to provide you with the means to get a _function value_ based on a name.
This is not what you are describing. I think name-based reflection could have
a use in utility or framework code, but relying on this kind of unrestricted
indirection is really just a sign of deeper problems, I think.

> _You're right, it's dangerous when used improperly_

Which uh... was my point.

> _but you should consider coming down from the theory clouds if you believe
> dynamic invocation and goto are in the same boat on the river styx._

Once again, you appear to be rebutting an argument I never made. Suggesting
that I'm some sort of aloof theoretician is really a bit presumptuous. I
developed an interest in all of those things you describe precisely _because_
I am a programmer. Sure, I know a reasonable amount about theory, but I don't
see that that should be used to discount any and all knowledge I have about
practical programming too.

Let me make very clear my objection, once again. I am going to leave out
exactly one definition from each of the following code snippets:

    
    
      foobar(0);
    

vs.

    
    
      $x(0);
    

I left out the definition of 'foobar' in the first snippet, and the definition
of '$x' in the second. Can _you_ tell me where program control goes in each
case?

In the first case, you know that it goes to a function named "foobar"
somewhere within scope.

In the second case, of course you can't! You have to go figure out how $x has
been computed. You basically have to read most of the code in order to find
out what value $x will have just to be able to predict what the program will
do next! This is basically the same problem that goto-style code creates - it
makes it considerably more difficult to mentally simulate the execution of the
program.

 _Edit: This is getting down-voted? Really? I would love to know why._

~~~
KrisJordan
Didn't intend to cause harm in "straw-man"ing. I think it's valuable to know
where people are coming from. You care enough about programming languages and
verification to have a blog on it, that's awesome. It's perspective.

No where in the post is there an example advocating dynamic invocation based
on computed strings. No where. In fact, the more I read your responses the
less I believe what you're saying has anything to do with the contents and
examples of the post. We're on the same page here: dynamic invocation with
computed strings: bad. spaghetti code. evil like eval.

> What does "dynamic invocation" have to do with higher-order functions?

Everything? Whether you're rolling your own or making use of PHP's built-in
higher order functions, like array_map <http://us.php.net/array_map>, you're
dynamically invoking something: a lambda, a function, an instance method, or a
static method. Unfortunately these are distinct things in PHP. So with
array_map its first argument is a callback, or something that returns true to
is_callable as described in the post. What you're arguing is that you should
never send a string name of a function to array_map, but should instead wrap
the invocation in a lambda. So..

    
    
      array_map(function($s) { return strtolowwer($s); }, $array);
    

Is preferable to:

    
    
      array_map('strtolowwer', $array);
    

Point blank, it's just not in PHP. It's more than twice as slow, it takes more
than twice the memory, and, most importantly is every bit as unsafe. Notice I
misspelled lower with an extra w? Let's imagine that's a bug, a programmer's
typo. Neither case will error out until array_map is executed which means both
parse and generate opcode just fine. Reconsider the function call wrapped with
in a lambda. What must it mean deep down for PHP to be able to generate valid
opcodes on a pure method call to a method that doesn't exist? It means the
internal opcode must simply store the method name as a string waiting to be
executed. So once we hit the op a table lookup based on a string in your code
happens. Both ways. One string is wrapped in quotes, the other in a far more
elaborate construct.

As for reflection, I'm all for it but in PHP a reflected function or method
object is not a callable, thus cannot be passed to a function like array_map.
Even if you could, I do not believe that, in PHP, the following is any less
evil:

    
    
      array_map(new ReflectionFunction('strtolowwer'), $array);
    

Again, more memory, more code to execute, won't error out until runtime.

I really don't think you or I disagree on anything in terms of "best
programming practices". The only thing we seem to disagree on is what this
post was about and trying to demonstrate/advocate.

~~~
gdp
> _No where in the post is there an example advocating dynamic invocation
> based on computed strings. No where._

Your example that uses a random number generator to select a string and then
use that string as a function name is dynamic invocation based on computed
strings.

> _What you're arguing is that you should never send a string name of a
> function to array_map, but should instead wrap the invocation in a lambda._

No, that's not what I'm arguing at all. Your example above uses a string
_literal_ , which is basically the same as having a first-class identifier in
its place. I don't think that kind of behaviour significantly harms
readability. However, there's really nothing more "dynamic" about this kind of
invocation than any other invocation in PHP.

I kinda question your use of "dynamic invocation" as a blanket term to cover
first-class anonymous function application, higher-order functions and
indirection. These are all separate-but-related concepts. My cursory Googling
didn't turn up any uses of 'dynamic invocation' that are anything like the way
you are using it - do you have a source or reference or is this your
terminology?

> _What must it mean deep down for PHP to be able to generate valid opcodes on
> a pure method call to a method that doesn't exist? It means the internal
> opcode must simply store the method name as a string waiting to be executed_

Perhaps I'm misunderstanding what the internals of the PHP interpreter are
these days, but you appear to be describing the use of a symbol table or
something. The use of 'opcode' confuses me, because if there was code
generation going on, it would be possible to statically resolve names to
first-class function values.

What I'm getting at is that I don't see that there is a difference (in PHP)
between the "dynamic invocation" you are describing, and straightforward run-
of-the-mill function-calling, given that PHP's internal representation of both
is essentially just a string (which is looked up in some symbol table in order
to resolve it to a piece of code).

And so my point is that there is no real reason to exploit this quirk of
language design in order to exploit indirection on computed strings (which, as
I said in my previous post, I consider to be a kind of goto-esque spaghetti
code feature).

