2

I wanna buffer some content. The way how the content is fetched depends, that's why I added a type parameter to my buffer function to define whether to include or to echo the source.

PHP

<?php

function bufferContent($source, $type = 'include') {
  ob_start();
  $type($source);
  return ob_get_clean();
}

echo bufferContent('<html>test</html>', 'echo');

?>

Output

Fatal error: Call to undefined function echo() in #### on line 5

Why's that? Isn't it possible to call a standard PHP function like echo() or include() by a string variable?

Edit: Changed question slightly to make it more suitable to the answers.

Ben
  • 43
  • 2
  • 6
  • The closest thing you can do is replace your `echo` with `print_r`.. It's not the same (with `print_r` you print not only strings) but... it will work. (`print` is a language construct as well) – acm Apr 08 '11 at 16:01

5 Answers5

6

echo is not a function : it is a language construct -- and, as such, it cannot be called this way.

A possibility for you would be to define a function, that would itself call echo -- and use your function when calling bufferContent :

function my_echo($str) {
    echo $str;
}
echo bufferContent('<html>test</html>', 'my_echo');


A a reference, quoting the manual page of echo :

Note: Because this is a language construct and not a function, it cannot be called using variable functions

Pascal MARTIN
  • 395,085
  • 80
  • 655
  • 663
2

You cannot call echo, include, require_once, isset, empty from string variable because they do not behave like normal functions. You can use

include "file.php";

AND

include("file.php");

You can make a wrapper function and call them instead like :

function wrap_echo($str) { echo($str); };

and do

$f = "wrap_echo";
$f("sth");
Radoslav Georgiev
  • 1,366
  • 8
  • 15
1
function buffer_content($source, $type = 'echo') {
    if(!is_string($type)){
        trigger_error('$type must be a string.', E_USER_WARNING);
        return false;
    }
    if(is_object($source) and method_exists($source, '__toString')){
        $source = strval($source);
    }elseif(is_scalar($source)){
        $source = strval($source);
    }elseif(!is_string($source)){
        trigger_error('$source must be a string as non-scalars do not echo nicely.', E_USER_WARNING);
        return false;
    }
    ob_start();
    switch(strtolower($type)){
        case 'include': include $source; break;
        case 'include_once': include_once $source; break;
        case 'require': require $source; break;
        case 'require_once': require_once $source; break;
        case 'echo': echo $source; break;
        default: trigger_error("\$type '{$type}' is not supported.", E_USER_WARNING); break;
    }
    return ob_get_clean();
}

^ you need to improvise a bit. This is how you do what you need done! But there are better and more efficient/versatile ways to do it.

CodeAngry
  • 12,760
  • 3
  • 50
  • 57
  • Why not use something like `$valid_types = array("include", "include_once", "require", "require_once"); ... in_array($type, $valid_types, true)...` along with a `switch/case` afterwards, that might make it more readable than all the `elseif(!strcasecmp())` – phant0m Nov 02 '12 at 10:07
  • @phant0m People tend to choke on `switch case break` more then on `if`s. If you forget the `break;`, you've got a problem. With `if` it's all contained nice and beginner friendly. Everybody understands `if` :) and `{...}` nicely delimited blocks. – CodeAngry Nov 02 '12 at 10:25
  • I don't think beginner-friendliness is a valid argument. In that case, you could make the same argument to abandon classes, references, ob_*, closures etc ;) "`But there are better and more efficient/versatile ways to do it.`" such as? :) *P.S.* You don't need the "P.S." note – phant0m Nov 02 '12 at 10:30
  • @phant0m Changed. _I feel stalked._ Am I under some kind of `SO` review :) – CodeAngry Nov 02 '12 at 10:37
  • Heh, no, I just noticed that you're new with promising posts ;) Last minor nitpick: You don't necessarily need the type check on `$source`, since echo could also print other things, and the length/trim check is also redundant. – phant0m Nov 02 '12 at 10:44
  • @phant0m Fixed. That's the most I can do. I won't, in all good conscience, echo non-scalars. – CodeAngry Nov 02 '12 at 10:50
1

Gonna get scolded for that, but the lazy workaround in your case would be:

eval(" $type(\$source); ");

That works with normal functions and language constructs. Though you really ought to be using a switch for the special cases, and keep the normal variable function call for everything else.

mario
  • 144,265
  • 20
  • 237
  • 291
  • 3
    +1 Better would be `function a($f) { if (in_array($f, array('echo', 'print', 'include', 'include_once', 'require', 'require_once'))) { eval($type . '($source)'); } else { $type($source); } }` in order to only eval some "functions" so no potential security vulnerability is introduced. – NikiC Apr 08 '11 at 16:09
  • 1
    Much appreciated, gens. I'm using `eval("$type('$source');");` now while checking $type before: `if (!in_array($type, array('include', 'echo', 'print'))) die('error message');` – Ben Apr 11 '11 at 07:53
0

You cannot call echo as a function because it is infact not a function but rather a language construct within PHP.

In order to effectivly call echo, you can create a wrapper method such as:

function call_echo($str){
  echo $str;
}

On the topic of calling functions via strings I'd use call_user_func

mixed call_user_func ( callback $function [, mixed $parameter [, mixed $... ]] )

so in your case it'd be

call_user_func($type, $source);

I'd choose call_user_func over a variable function because it is more readable and less confusing. If I were reading your code, I'd immediately see what you were doing if you called call_user_func

Mike Lewis
  • 63,433
  • 20
  • 141
  • 111