Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
In my code there are 3 sections. Top and bottom section must have to execute. Middle portion have to hide if input code is wrong.
This is my code:
//correct code section. Must execute
echo "must show top";
//may contain wrong code. But this portion should not affect on lower
portion code
ob_start();
// sample wrong code
function jk($i){};
function jk($i){ echo $i; }';
ob_end_clean();
//correct code section. Must execute
echo "must show bottom";
Current output:
Fatal error: Cannot redeclare jk() (previously ......
Expected output:
must show top must show bottom or must show top
Fatal error: Cannot redeclare jk() (previously ......
must show bottom
You can't do that in this way, because the PHP file is compiled (to bytecode, but whatever) before any part of it is executed.
You can handle minor errors by wrapping the middle part in try/catch, but fatal errors will still make the whole script not work.
So what you can do is to put the middle code somewhere else, and then access it from outside the whole process scope. This is possibly tricky if the two parts need to exchange information (e.g. some variables).
The simplest way to do that is to save the code into a temporary file, then access it from within PHP by using URL get_file_contents:
// Construct PHP tag -- hiding it from editor :-)
$prolog = '<'; $prolog .= '?'; $prolog .= "php\n";
// prolog code contains PHP code, but here we treat is as text,
// so all $'s must be escaped. Same goes for epilog.
$prolog .= <<<PROLOG
// This is common code to make what follows work.
// We can pass any data to this script by creating a *second* file
// and passing the name of this second file via GET to avoid POST
// size limits.
// To avoid arbitrary inclusion (just in case), we could check that
// the file name thus passes conforms to a given syntax, e.g. 32
// random hex characters, no URLs or /etc/passwd or funny stuff.
if (isset(\$_GET['envfile'])) {
\$name = basename(\$_GET['envfile']) . '.txt';
\$data = unserialize(file_get_contents(\$name));
unlink(\$name);
} else {
\$data = null;
}
PROLOG;
$epilog = <<<EPILOG
// The code is now hopefully loaded and defines a main()
// function.
if (!function_exists('main')) {
die("-ERR: no main function");
}
try {
die(json_encode(main(\$data)));
} catch (\Exception \$err) {
die("-ERR: main failed: {\$err->getMessage()}");
}
EPILOG;
$unique = uniqid();
// "special dir" is NOT available from THIS site, and is the webroot of another "sandbox" web site
$physical = "./special-dir/{$unique}.php";
// file must contain a full PHP script with tags and all
file_put_contents($physical, $prolog . $code . $epilog);
// Now ask the file to the web server with its "sandbox" personality
$response = file_get_contents("http://sandbox/{$unique}.php");
The above uses a virtual host which should be only accessible from localhost itself, and which (in addition to careful security: sensitive functions disabled, no access outside its own webroot, ...) should have full debugging information and error reporting turned on.
So if you get a PHP error page, you know that an error occurred (and you might even parse it). Otherwise, the script might return e.g. a JSON-encoded variable object which you could import:
if (0 === strpos($response, '-ERR')) {
// $response is a simple error message
}
$package = json_decode($response, true);
if (false === $package) {
// $response is probably a fatal error HTML page
}
// Do something with $package.
So to recap:
get the code from user
mix it with appropriate prolog/epilog stubs to e.g. prime some variables to make them available to the "child" script, and return data as JSON
save the resulting code into a temporary file within the webroot of
a secured "sandbox" website with full debug
invoke the sandbox URL to have a second PHP interpreter spawn and look at the code
interpret the results
NOTE: in PHP 7, the new code parse tree approach could perhaps allow you to validate the code directly, without the need of saving it and sending to a new PHP interpreter. Then if it's OK you could eval() it directly, but take care that doing so executes the code in the current script's scope, which has higher privileges. You are quite likely to have your sandbox pwn3d and/or abused in really short order.
Related
while using Codacy to analyze my PHP code I found a number of errors caused by the exit(); function. here's one function,
public function saveCssForm(){
$data = $_POST;
if(!$data){
// is a direct acess
$this->index();exit();
}
// update the data
$this->csssettingmodel->updateCSS($data);
// save the notifications
$this->notify_update($data['site_id'],$data['lang_key']);
// set the success message
$this->session->set_flashdata('edit_item', 'edited');
// redirect to the view page
$baseUrl = $this->config->item('base_url');
redirect($baseUrl.'index.php/cssSettings/view/'.$this->session->userdata("languageabbr"));
}
public function index()
{
// Denay Direct Access
echo "<hr><h1><center>NO DIRECT ACCESS</h1> </center>";
echo "<center>You are not permitted to access this page </center>";
}
and the codacy result shows this...
any alternatives or suggestions to avoid this would be helpful.
Codacy is not displaying errors, in the sense of problems you need to fix; it is analysing the quality of your code, and suggesting that the exit appearing in this position is not a good practice, so you might want to fix it.
Firstly, application frameworks are often designed to have a single point of entry, process some logic, and then return a result to the entry point which will output and clean up. Exiting from different points in the code makes it harder to predict the flow, because whole sections of the code may look reachable but actually come after the program has exited.
Secondly, such code might be used for debugging, to interrupt the flow of execution at a particular point to display some intermediate data or simulate a particular failure. In this case, it appearing in the analysed code would suggest that you had left the debugging code in accidentally.
Note: While the issue occurred trying to solve a WordPress problem, the problem being faced right now (see "Current Approach", below) is clearly a PHP issue, which is why it's been posted here.
TL;DR:
I'm dynamically generating PHP code in a file and including that file after it's finished being written to, however PHP cannot call functions from inside the file—even when including it directly without making any changes.
Preface
I've been working on a system for dynamically generating shortcodes in WordPress, and have been running into one serious roadblock along the way: Registering the functions for the shortcodes.
Long story short is that we've got a list of options stored in an array similar to the following:
$array = array(
0 => array(
'code' => 'my-shortcode',
'replacement' => 'replacement_directives_here'
)
);
I've already got a system that processes the codes and sends generates the proper output based on the directives. It's getting the call to add_shortcode($shortcode, $callback) to work.
My first strategy was to use anonymous functions in a manner similar to the following:
foreach ($array as $code => $directions) {
$GLOBALS['my_shortcode_output'] = my_shortcode_process($directions);
add_shortcode($code, function() { return $GLOBALS['my_shortcode_output']; });
}
However this ended up with successive directives overwriting each other due to the fluctuating content of the global, so I decided to try something different...
Current Approach
As a workaround to having the information I need constantly slipping just out of reach, I decided to try something different:
Create a file within my plugin
Write out the PHP code for the functions I wanted to include so that they were guaranteed to generate the right output
Include the file after the fclose() call
Register the shortcodes, referencing the functions in the new file
The code for generating the file looks something like the following:
$file = fopen(PATH_TO_FILE, 'w'); // Open for writing only, truncate file on opening
fwrite($file, "<?php\n\n"); // Using double quotes so newlines and escapes don't get counted as literals
// foreach through list
foreach ($shortcode_list as $shortcode) {
if ($shortcode['code'] != '') {
// Append new function with a dynamic name (e.g. $shortcode[code]._sc') to dynamic_shortcodes.php
// Function should consist of: return my_shortcode_process($shortcode['replacement']);
// Hard-coding so we don't get frozen if information gets changed
$new_function = "\tfunction str_replace('-', '_', $shortcode['code'])."_sc() {\n\t\treturn my_shortcode_process('".$shortcode['replacement']."');\n\t}\n\n";
fwrite($file, $new_function);
// Add function name to $shortcode_functions array, keyed on the shortcode
$shortcode_functions[$shortcode['code']] = str_replace('-', '_', $shortcode['code']).'_sc';
}
}
fclose($file); // Close the file, since we are done writing to it
touch(PATH_TO_FILE); // Ensure the file's modification time is updated
After that it's just a simple loop to register them:
foreach ($shortcode_functions as $shortcode => $callback) {
add_shortcode($shortcode, $callback);
}
When I reload and run everything, however, I get the following:
Notice: do_shortcode_tag was called incorrectly. Attempting to parse a shortcode without a valid callback: [SHORTCODE HERE]. Please see Debugging in WordPress for more information.
I've downloaded the file and verified its contents—everything checks out in PHPstorm. It's properly formatted, there are no syntax errors, and the functions it depends upon are already loaded and working fine.
Even skipping the whole process and just directly including the file and then calling one of the functions inside it isn't working. It's like the file has been blackballed. At the same time, however, neither include() nor require() produce any errors.
Is this a caching issue, perhaps?
Honestly, I'm totally unable to understand what you're trying to do (or even why) but PHP doesn't have any problem with dynamically generated includes. This works fine:
<?php
$file = __DIR__ . '/foo-' . mt_rand(0, PHP_INT_MAX) . '.php';
$code = '<?php
function foo() {
echo "Hello, World!";
};
';
file_put_contents($file, $code);
require $file;
foo();
However, you're apparently trying to execute something like this:
function str_replace('-', '_', $shortcode['code'])."_sc(){
}
That's a blatant syntax error either in included files on in the main script. If you follow the Please see Debugging in WordPress for more information instructions you'll possibly find something similar to this:
Parse error: syntax error, unexpected ''-'' (T_CONSTANT_ENCAPSED_STRING), expecting variable (T_VARIABLE)
A syntax for variable functions that works could be:
$name = str_replace('-', '_', $shortcode['code']) . '_sc';
$name = function (){
};
OK most the answers on S.O. are BACKWARDS from what I am having an issue with. My variable is not seen FROM the included file. I have never ran into this, and it is messing everything up. I was using smarty, but since it cant see the var, I went back to old fashion php templating, and still cannot see the var.. here is the scripts in question (a gaming site ofc).
INCLUDED FILE: prep_feature_game.php
foreach($games->gamexml->game as $game){
$d = parseDate($game['releasedate']);
$game['date_month'] = $d['month'];
$game['date_day'] = $d['day'];
$game['date_year'] = $d['year'];
$game['image_60x40'] = showImage($game['foldername'],'60x40');
$game['genrelist'] = displayGenreList($game['gameid']);
//debugPrint($game);die();
$game_list[] = $game;
}
CONTROL PAGE:
switch ($page) {
default:
include('includes/prep_feature_game.php');
// NOTE $GAME_LIST HAS THE ARRAY ^
display('feature_game');
break;
AND THE DISPLAY FILE: FEATURE_GAME.PHP
<h1><?=SITE_NAME;?>'s Featured Games!</h1>
<table>
<?PHP
// TEST FOR $GAME_LIST
foreach($game_list as $field=>$data){
echo $data['gamename'];
}
/*
this produces the error:
Notice: Undefined variable: game_list in C:\xampp\htdocs\ix2\themes\missingpiece\feature_game.php on line 7
*/
}// end switch page
I have many variations over the years of these templates, and the "included" files have never produced this kind of error. I know that included files act as though the script contained is inserted where the include is at.
why is the receiving script unable to see the variable produced from the included script. These are all included, so it should not be an issue
PS
I noticed this started happening when I upgraded from php v5 - php v7, doubt it has anything to do with that, I havent checked that yet since include files are pretty standard.
Your question is not showing the definition of the display function, but unless you declare $game_list as a global within that function it is out of scope. If you include the template it'll work.
include('includes/prep_feature_game.php');
include('feature_game.php');
Or add parameters to your display function.
display('feature_game', $game_list);
Or if you're using smarty you must assign the variable first.
$smarty->assign('game_list', $game_list);
$smarty->display('feature_game.tpl');
I am new to PHP and very likely I am using the incorrect approach because I am not used to think like a PHP programmer.
I have some files that include other files as dependencies, these files need to have global code that will be executed if $_POST contains certain values, something like this
if (isset($_POST["SomeValue"]))
{
/* code goes here */
}
All the files will contain this code section, each one it's own code of course.
The problem is that since the files can be included in another one of these files, then the code section I describe is executed in every included file, even when I post trhough AJAX and explicitly use the URL of the script I want to POST to.
I tried using the $_SERVER array to try and guess which script was used for the post request, and even though it worked because it was the right script, it was the same script for every included file.
Question is:
Is there a way to know if the file was included into another file so I can test for that and skip the code that only execute if $_POST contains the required values?
Note: The files are generated using a python script which itself uses a c library that scans a database for it's tables and constraints, the c library is mine as well as the python script, they work very well and if there is a fix for a single file, obviously it only needs to be performed to the python script.
I tell the reader (potential answerer) about this because I think it makes it clear that I don't need a solution that works over the already existant files, because they can be re-generated.
From the sounds of it you could make some improvements on your code structure to completely avoid this problem. However, with the information given a simple flag variable should do the trick:
if (!isset($postCodeExecuted) && isset($_POST["SomeValue"]))
{
/* code goes here */
$postCodeExecuted = true;
}
This variable will be set in the global namespace and therefore it will be available from everywhere.
I solved the problem by doing this
$caller = str_replace($_SERVER["DOCUMENT_ROOT"], "", __FILE__);
if ($_SERVER["REQUEST_METHOD"] === "POST" and $caller === $_SERVER["PHP_SELF"])
performThisAction();
From the manual:
void __halt_compiler ( void )
This function halts the execution of the compiler. This can be useful to embed data in PHP scripts, like the installation files.
Note: __halt_compiler() can only be used from the outermost scope.
Can anyone provide an actually case where this function is useful?
Assume you have one script with some php code and lots and lots of binary clutter.
<?php doStuff(); __halt_compliler(); [BIG_BINARY_MESS]
then you want the compiler to NOT try to parse the binary because if there is <? somewhere in the binary it would break.
The point is being able to just ship one file with binary data and php code.
For a little example see this blog post
So you want not only to stop the execution of a script (like exit() would) but to stop the parsing so that you can have "invalid syntax" at the end of file and php still can execute the first part.
Another example:
This will get parsed as valid php and execute just fine:
<?php $a = 1; echo $a; __halt_compiler(); §RW$FG$%ZDS$TSG$TSZ%U(); §$"§%"§$!!();
To access the data:
<?php
$file = fopen(__FILE__, 'rb');
// Go to the end of the __halt_compiler();
fseek($file, __COMPILER_HALT_OFFSET__);
echo stream_get_contents($file);
__halt_compiler(); §RW$FG$%ZDS$TSG$TSZ%U(); §$"§%"§$!!();
This will output §RW$FG$%ZDS$TSG$TSZ%U(); §$"§%"§$!!();
Previously, The ClassGenerator in the PhpSpec unit testing library provided a good example of using __halt_compiler(), which the PHP class contains a code template for a PHP class.
They've recently update to read the template from a seperate file, but initially the getTemplate() method will attempt to read the PHP code template provided in the file that follows the __halt_compiler() call. This avoids the <?php token from getting parsed.
/**
* The Class Generator is responsible for generating the classes from a resource
* in the appropriate folder using the template provided
*/
class ClassGenerator
{
//...
/**
* #return string
*/
protected function getTemplate()
{
return file_get_contents(__FILE__, null, null, __COMPILER_HALT_OFFSET__);
}
}
__halt_compiler();<?php%namespace_block%
class %name%
{
}
Here's another possible use. I have a long file of PHP functions. Many of these aren't currently valid, but might be required soon. I want to disable them, but not entirely delete them. I want the code to remain visible to all developers so that they can reinstate the code if they need it. Also anyone searching via grep would still find the code.
So I move the code to the end of the file, and want to "comment it out". However the functions themselves have comments in. So I would need to start a new block comment after the end of each block comment in the original code. __halt_compiler(); does the job without changing any other lines.
(If I delete the code and commit that to a version control system, that does allow me to reinstate the code, but it wouldn't be visible to other developers unless they new to look.)