开发者

How can I avoid showing "#!/usr/bin/php" on PHP?

开发者 https://www.devze.com 2023-01-24 03:51 出处:网络
I want PHP scripts to run both on command li开发者_JAVA百科ne and website (I use Apache and Nginx) so I put #!/usr/bin/php in the first line of my scripts but that appears on the website...I solved th

I want PHP scripts to run both on command li开发者_JAVA百科ne and website (I use Apache and Nginx) so I put #!/usr/bin/php in the first line of my scripts but that appears on the website...


I solved the problem using output buffering. My script now looks like this:

#!/usr/bin/php
<?php
@ob_end_clean();
...

Note: There is no ?> at the end of the file. This is actually a good practice when writing PHP scripts. This prevents any garbage text to be accidentally printed.

Note: The PHP documentation for ob_end_clean() says that:

The output buffer must be started by ob_start() with PHP_OUTPUT_HANDLER_CLEANABLE and PHP_OUTPUT_HANDLER_REMOVABLE flags. Otherwise ob_end_clean() will not work.

It seems that this is done automatically when PHP runs from command line.


There is no need to have #!/usr/bin/php in your code, just run CLI script using php, for example php /path/to/file.php or /usr/bin/php /path/to/file.php.


@ViliamSimko's wicked trick is almost there, but, unfortunately, flawed. (It did actually break my header-sending sequence, for instance, despite not polluting the output with the shebang.)

TL;DR, here's the fix*:

#!/usr/bin/php
<?php @ob_end_clean(); if(ini_get('output_buffering')) ob_start();
...

or, less obscene-looking, but still just an euphemism for the same offense ;) :

#!/usr/bin/php
<?php if (ob_get_level()) { ob_end_clean(); ob_start(); }
...

(And see also the "UPDATE" part below for perhaps the maximum we can do about this...)

Explanation:

@Floris had a very good point in the comments there:

Do you need an ob_start(); as well? Probably worth mentioning.

You sure do. But where? And when?

Cases to consider:

  1. As Viliam said (and just confirmed it with PHP 7.2), the shebang is fortunately eaten by the php command itself, when you run your script with php yourscript.php, so the entire trick is redundant.

  2. In web mode, it's actually config-dependent: if output_buffering is on in the config (and sure enough, it's usually on, but it's not even the default), an ob_start has already been done implicitly at the start of the script (you can check it with ob_get_level()). So, we can't just abruptly cancel it with an ob_end_clean and call it a day: we need to start another one, to keep the levels balanced!

  3. If output_buffering is off in the config, then, sad face, we are out of luck: ob_get_clean() does nothing, and the shebang will end up in the top of the page.

    Note: there is no fix for this one, other than turning it on.

  4. In command-line mode, the manual says about output_buffering:

    This directive is always Off in PHP-CLI.

    But, instead of failing the same hopeless way as in 3., the implicit shebang cleanup (see 1.) saves the day.


* "Fix" in the sense that this audacious hack will work in a lot more cases. If you are in full control of your PHP env., it can be just fine (as is in my case). Otherwise, it can still break in lots of subtle ways unexpectedly (consider auto-prepended code, custom extensions, or other possible ways to control output buffering etc.). Also, for example, when included from other scripts in CLI mode (where there's no buffering), you are still out of luck: the shebang will show up in the output, no matter what (unless, of course, filtered out manually by the caller). Not only that, but it would also break up your own buffering, if you happened to have any, while including such a naughtified script.


UPDATE: Just for the fun of it, here's an "almost correct" version, which plays along nicely with an ongoing buffering, be it implicit or user-level:

#!/usr/bin/php
<?php
if (ob_get_level()) {
    $buf = ob_get_clean();
    ob_start();
    // Refill the buffer, but without the shebang line:
    echo substr($buf, 0, strpos($buf, file(__FILE__)[0]));
} // else { out of luck... }

Still only "almost" correct, as nothing can fix output_buffering = 0 in web mode, and the "being included with no buffering" case can only be solved if the calling script adds an explicit ob_start - ob_end_... wrapping. Also, most of the caveats above still apply: various subtleties can still break it (e.g. the current output buffer must have the (fortunately default) PHP_OUTPUT_HANDLER_CLEANABLE flag etc.).)


I generally find it a good idea to separate logic from presentation. When I do something like this, I put as much as possible in a library, and then write separate cli and web interfaces for it.

That said, calling it with the php command is probably an easier fix.


Call the script using the php command


The output buffering solution above is a hack. Don't do that.

First thing, you are actually better using the env command to determine which php is being used:

#!/usr/bin/env php

Then give it permission to be executed by itself:

chmod +x myfile

So instead of calling 'php myfile', you now just run:

./myfile

From that folder. Hope this helps!

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号