If the code is the same, there appears to be a difference between:
include 'external.php';
and
eval('?>' . file_get_contents('external.php') . '<?php');
What is the difference? Does anybody know?
I know the two are different because the include
works fine and the eval
gives an error. When I originally asked the question, I wasn't sure whether it gave an error on all code or just on mine (and because the code was eval
ed, it was very hard to find out what the error meant). However, after having researched the answer, it turns out that whether or not you get the error does not depend on the code in the external.php
, but does depend on your php settings (short_open_tag
to be precise).
As noted by @bwoebi in this answer to my question, the
eval
substitution does not respect the file path context of the included file. As a test case:Baz.php
:Foo.php
:Result of executing
php Foo.php
:I don't know of any way to change the
__FILE__
constant and friends at runtime, so I do not think there is any general way to defineinclude
in terms ofeval
.After some more research I found out what was wrong myself. The problem is in the fact that
<?php
is a "short opening tag" and so will only work ifshort_open_tag
is set to 1 (in php.ini or something to the same effect). The correct full tag is<?php
, which has a space after the second p.As such the proper equivalent of the include is:
Alternatively, you can leave the opening tag out all together (as noted in the comments below):
My original solution was to add a semicolon, which also works, but looks a lot less clean if you ask me:
Only
eval('?>' . file_get_contents('external.php'));
variant is correct replacement for include.See tests:
Output:
If you are using a webserver on which you have installed an opcode cache, like APC,
eval
will not be the "best solution" : eval'd code is not store in the opcode cache, if I remember correctly (and another answer said the same thing, btw).A solution you could use, at least if the code is not often changed, is get a mix of code stored in database and included code :
I've worked with software that uses this solution (the on-disk file being no more than a cache of the code stored in DB), and I worked not too bad -- way better that doing loads of DB requests of each page, anyway...
Some not so good things, as a consequence :
BTW : would I dare saying something like "eval is evil" ?
Some thoughts about the solutions above:
Temporary file
Don't. It's very bad for performance, just don't do it. Not only does it drive your opcode cache totally crazy (cache hit never happens + it tries to cache it again every time) but also gives you the headache of filesystem locking under high (even moderate) loads, as you have to write the file and Apache/PHP has to read it.
Simple eval()
Acceptable in rare cases; don't do it too often. Indeed it's not cached (poor opcode cache just doesn't know it's the same string as before); at the same time, if your code is changing each time, eval is A LOT BETTER than include(), mostly because include() fills up the opcode cache on each call. Just like the tempfile case. It's horrible (~4x slower).
In-memory eval()
Actually, eval is very fast when your script is already in the string; most of the time it's the disk operation that pulls it back, now surely this depends on what you do in the script but in my very-small-script case, it was ~400 times faster. (Do you have memcached? Just thinking loud) So what include() can't do is evaluate the same thing twice without file operation, and this is very important. If you use it for ever-changing, small, memory-generated strings, obviously it's eval to choose - it's many-many times faster to load once + eval again and again than an iterated include().
TL;DR
This lets you include a file assuming file wrappers for includes is on in PHP:
... Then include that 'file.' Yes, this will also disable opcode caches, but it makes this 'eval' the same as an include with respect to behavior.