This is a bit of a long shot, but I figured I'd ask anyway. I have an application that has web-based code editing, like you find on Github, using the ACE editor. The problem is, it is possible to edit code that is within the application itself.
I have managed to detect parse errors before saving the file, which works great, but if the user creates a runtime error, such as MyClass extends NonExistentClass, the file passes the parse check, but saves to the filesystem, killing the application.
Is there anyway to test if the new code will cause a runtime error before I save it to the filesystem? Seems completely counter-intuitive, but I figured I'd ask.
Possibly use register_shutdown_function to build a JSON object containing information about the fatal error. Then use an AJAX call to test the file; parse the returned value from the call to see if there is an error. (Obviously you could also run the PHP file and parse the JSON object without using AJAX, just thinking about what would be the best from a UX standpoint)
function my_shutdown() {
$error = error_get_last();
if( $error['type'] == 1 ) {
echo json_encode($error);
}
}
register_shutdown_function('my_shutdown');
Will output something like
{"type":1,"message":"Fatal error message","line":1}
Prepend that to the beginning of the test file, then:
$.post('/test.php', function(data) {
var json = $.parseJSON(data);
if( json.type == 1 ) {
// Don't allow test file to save?
}
});
Possibly helpful: php -f <file> will return a non-zero exit code if there's a runtime error.
perhaps running the code in a separate file first and attach some fixed code on the bottom to check if it evaluates?
Related
There's a lot of code in each file, too much to post, so I'm giving you a general idea of what's happening in each file.
index.php
[html dropdown menu code etc.]
scripts.js
[AJAX detects user selection from dropdown, grabs fetch.php which pulls database to generate html code for secondary dropdown selections to put in index.php]
fetch.php
[Generates secondary dropdown code based on user selection and query of the database]
I need to see what exactly is being queried to debug, so I'd like to echo the sql select statement:
$query = "SELECT * FROM databasename WHERE.."
That is in fetch.php when user makes a selection from index.php - How do I do this?
When I deal with AJAX, that I return as JSON, one trick I use is to take advantage of output buffering. You can't just echo or output anything you want because it will mess up the JSON data so for an example,
ob_start(); //turn on buffering at beginning of script.
.... other code ...
print_r($somevar);
.... other code ...
$debug = ob_get_clean(); //put output in a var
$data['debug'] = $debug;
header('Content-Type: application/json');
echo json_encode($data); //echo JSON data.
What this does, is wrap any output from you script into your JSON data so that it's format is not messed up.
Then on the javascript side you can use console.log
$.post(url, input, function(data){
if(data.debug) console.log(data.debug);
});
If you are not used to debugging with console.log(), you can usually hit F12 and open the debugger in most browsers. Then in there the output will be sent to the "console". IE9 had a bit of an issue with console.log() if I recall, but I don't want to go to far off track.
NOTE: Just make sure to not leave this stuff in the code when you move it to production, its very simple to just comment this line out,
//$data['debug'] = $debug;
And then your debug information wont be exposed in production. There are other ways to automatically do this, but it depends on if you do development local then publish to the server. For example you can switch it on the $_SERVER['SERVER_ADDR']; which will be ::1 or 127.0.0.1 when it's local. This has a few drawbacks, mainly the server address is not available from the Command Line Interface (CLI). So typically I will tie it into a global constant that says what "mode" the site is in (included in the common entry point, typically index.php).
if(!defined('ENV_DEVELOPMENT')) define('ENV_DEVELOPMENT','DEVELOPMENT');
if(!defined('ENV_PRODUCTION')) define('ENV_PRODUCTION','PRODUCTION');
if(!defined('ENVIRONMENT')) define('ENVIRONMENT',ENV_DEVELOPMENT);
//site is in Development mode, uncomment for production
//if(!defined('ENVIRONMENT')) define('ENVIRONMENT',ENV_DEVELOPMENT);
Then it is a simple matter to check it:
if(ENVIRONMENT == ENV_PRODUCTION ) $data['debug'] = $debug;
If you know how to use error reporting you can even tie into that using
if(ini_get('display_errors') == 1) $data['debug'] = $debug;
Which will only show the debug when display errors is on.
Hope that helps.
UPDATE
Because I mentioned it in the comments, here is an example of it wrapped in a class (this is a simplified version, so I didn't test this)
class LibAjax{
public static function respond($callback, $options=0, $depth=32){
$result = ['userdata' => [
'debug' => false,
'error' => false
]];
ob_start();
try{
if(!is_callable($callback)){
//I have better exception in mine, this is just more portable
throw new Exception('Callback is not callable');
}
$callback($result);
}catch(\Exception $e){
//example 'Exception[code:401]'
$result['userdata']['error'] = get_class($e).'[code:'.$e->getCode().']';
//if(ENVIRONMENT == ENV_DEVELOPMENT){
//prevents leaking data in production
$result['userdata']['error'] .= ' '.$e->getMessage();
$result['userdata']['error'] .= PHP_EOL.$e->getTraceAsString();
//}
}
$debug = '';
for($i=0; $i < ob_get_level(); $i++){
//clear any nested output buffers
$debug .= ob_get_clean();
}
//if(ENVIRONMENT == ENV_DEVELPMENT){
//prevents leaking data in production
$result['userdata']['debug'] = $debug;
//}
header('Content-Type: application/json');
echo self::jsonEncode($result, $options, $depth);
}
public static function jsonEncode($result, $options=0, $depth=32){
$json = json_encode($result, $options, $depth);
if(JSON_ERROR_NONE !== json_last_error()){
//debug is not passed in this case, because you cannot be sure that, that was not what caused the error. Such as non-valid UTF-8 in the debug string, depth limit, etc...
$json = json_encode(['userdata' => [
'debug' => false,
'error' => json_last_error_msg()
]],$options);
}
return $json;
}
}
Then when you make a AJAX response you just wrap it like this (note $result is pass by reference, this way we don't have to do return, and in the case of an exception we update $result in "real time" instead of on completion)
LibAjax::respond( function(&$result){
$result['data'] = 'foo';
});
If you need to pass additional data into the closure don't forget you can use the use statement, like this.
$otherdata = 'bar';
LibAjax::respond( function(&$result) use($otherdata){
$result['data'][] = 'foo';
$result['data'][] = $otherdata;
});
Sandbox
This handles catching any output and puts it in debug, if the environment is correct (commented out). Please pleas make sure to implement some kind of protection so that the output is not sent to clients on production, I cant stress that enough. It also catches any exceptions puts it in error. And it also handles the header and encoding.
One big benefit to this is consistent structure to your JSON, you will know (on the client side) that if if(data.userdata.error) then you have an exception on the back end. It gives you one place to tweak your headers, JSON encoding etc...
One note in PHP7 you'll have to or should add the Throwable interface (instead of Exception). If you want to catch Error and Exception classes Or do two catch blocks.
Let's just say I do a lot of AJAX and got sick of re-writing this all the time, my actual class is more extensive then this, but that's the gist of it.
Cheers.
UPDATE1
One thing I had to do for things to display was to parse the data variable before I console.log() it
This is typically because you are not passing the correct header back to the browser. If you send (just before calling json_encode)
header('Content-Type: application/json');
This just lets the browser know what type of data it is getting back. One thing most people forget is that on the web all responses are done in text. Even images or file download and web pages. It's all just text, what makes that text into something special is the Content-Type that the browser thinks it is.
One thing to note about header is you cannot output anything before sending the headers. However this plays well with the code I posted because that code will capture all the output and send it after the header is sent.
I updated the original code to have the header, I had it in the more complex class one I posted later. But if you add that in it should get rid of the need to manually parse the JSON.
One last thing I should mention I do is check if I got JSON back or text, you could still get text in the event that some error occurs before the output buffering is started.
There are 2 ways to do this.
If Data is a string that needs to be parsed
$.post(url, {}, function(data){
if( typeof data == 'string'){
try{
data = $.parseJSON(data);
}catch(err){
data = {userdata : {error : data}};
}
}
if(data.userdata){
if( data.userdata.error){
//...etc.
}
}
//....
}
Or if you have the header and its always JSON, then its a bit simpler
$.post(url, {}, function(data){
if( typeof data == 'string'){
data = {userdata : {error : data}};
}
if(data.userdata){
if( data.userdata.error){
//...etc.
}
}
//....
}
Hope that helps!
UPDATE2
Because this topic comes up a lot, I put a modified version of the above code on my GitHub you can find it here.
https://github.com/ArtisticPhoenix/MISC/blob/master/AjaxWrapper/AjaxWrapper.php
Echo the contents and do a die() or exit; afterwards... then in the Network tab of your browser, start it recording, run the Ajax request (it'll fail) but check the resource/name and then view the Response, and it'll show you what was echo'd in the script
Taken from: Request Monitoring in Chrome
Chrome currently has a solution built in.
Use CTRL+SHIFT+I (or navigate to Current Page Control > Developer > Developer Tools.
In the newer versions of Chrome, click the Wrench icon > Tools > Developer Tools.) to enable the Developer Tools.
From within the developer tools click on the Network button. If it isn't already, enable it for the session or always.
Click the "XHR" sub-button.
Initiate an AJAX call.
You will see items begin to show up in the left column under "Resources".
Click the resource and there are 2 tabs showing the headers and return content.
Other browsers also have a Network tab, but you will need to use what I commented to get the string value of the query.
ArtisticPhoenix solution above is delightful.
I am very new to php and I tried to write this function. Now it seems like the function is not Defined. Nothing happens when I open the php file and if I try to use console to run it. It gives an error --
contentcheck('ex1.php','Bajestani')
ReferenceError: contentcheck is not defined
The Code is below.
<?php
if(contentcheck('ex1.php','Bajestani')===true)
echo 'Got it';
function contentcheck($filename,$phrase)
{
$content = shell_exec('C:\xampp\htdocs\docman\pdftotext '.$filename.' -');
if (strpos($content,$phrase) !== false)
{
return true;
}
else
return false;
}
if(contentcheck('ex1.php','Bajestani')===true)
echo 'Got it';
?>
Thanks In advance
You state that you try to run the function from the console.
In addition, ReferenceError: contentcheck is not defined is a Javascript error, not a PHP error.
Both of these facts lead me to the conclusion that you are trying to run the PHP code from inside the browser.
Please note that PHP code is not available from within the browser -- the function will indeed be undefined if you run it in the console, because PHP is run on the web server, not in the browser. The browser will never see your PHP functions; it simply sees the output of the PHP program (eg the HTML code, etc that is printed from by your PHP program). The PHP code itself is never seen by the browser.
It's not entirely clear what your program is supposed to be doing but what is clear is that the way you're trying to run it is not going to work. You're going to have to re-think this one completely, and possibly learn a bit more about how client/server systems work, and PHP in particular.
I have a problem where I call a PHP function on page load - the function checks to see if a file exists it returns the filename, if it doesn't exist it runs a script which is fairly resourceful and takes time - converting a waveform image from an audio file. The problem is the audio files are large so creating the file can take some time, so if the audio file doesn't have this image file associated with it the page load takes as long as the process does.
What I'm after is for this function to return a placeholder image if one doesn't exist, but carry on with the process after the page is loaded - or in the background. So in theory when the page is reloaded at a later date the correct image will be there.
I can get the return of the placeholder image currently but then the process stops and the image doesn't get generated. Here's what I have so far:
function example($file_path, $file_name) {if ($file_path) {
if (file_exists("/path/to/folder/{$file_name}.png")) {
return "/path/to/folder/{$file_name}.png";
}
if (!file_exists("/path/to/folder/{$audio_file_name}.png")) {
return "/path/to/folder/processing.png";
Some stuff in here
return $new image
} return FALSE
As you can see this just stops when the file doesn't exist but I want the stuff in here to continue in background. Is it possible or do I need a different approach? Like a cron job or something? Any help appreciated.
You might try a queuing system like resque https://github.com/chrisboulton/php-resque
You then can generate a job, that processes the information and quite fast return with the "processing" image.
With this approach you won't know when it is finished though.
In my experience this is still easier than arguing with the operations guys to compile php with multi threading support.
I'd do it with AJAX. If the image is found, just put it there.
Otherwise, put the placeholder, and add a JS flag with data to load the waveform image.
In the PHP code that generates HTML Document, no conversion happens. And you have another request handler to handle requests coming from JS, that makes the conversion with suppied data.
The data created originally on HTML Document generation code will be passed to JS, which will use it to send a request for the conversion. While JS waits for response, you handle to loading time, and when response comes you put it on the placeholder.
If you're running on FastCGI / FPM you could consider doing the following:
You put a regular <img> tag with the src attribute pointing to your script.
If your script needs to regenerate, you make the browser redirect to a processing image.
If the image is ready, you redirect to the created image (you could do an AJAX poll on the page as well)
How to do step 2?
Normally, the browser has to wait for your script to end before performing a render or redirect; but FastCGI (PHP-FPM) has a special function for this: fastcgi_finish_request. It's largely undocumented, but its use is simple:
if ($need_to_process) {
header('Location: /path/to/processing.png');
fastcgi_finish_request();
// do processing here
} else {
header('Location: /path/to/final_image.png');
}
Alternative
You can apply it to your existing process as well if you have a template that you can immediately render just before doing fastcgi_finish_request().
Yet another alternative
Use a task scheduler like Gearman.
you can use "try" and "finally"
try {
return "hello world";
} finally {
//do something
}
I am not able to comment because my reputation is below 50, but I wanted to note something on mohammadhasan's answer. It seems to work but avoid 'return' statement in both try and finally block
try {
return "hello world";
} finally {
//do not put return here
}
Example:
function runner() {
try {
return "I am the trial runner";
} finally {
return "I am the default runner";
}
}
echo runner();
Will only show I am the default runner.
I have a PHP script that creates other PHP files based on user input. Basically, there are files containing language specific constants (define) that can be translated by the user. In order to avoid runtime errors, I want to test newly written files for parse errors (due to "unusual" character sequences). I have read several posts here on SO (like PHP include files with parse errors) and tried a function that uses
$output = exec("php -l $filename");
to determine whether a file parses correctly. This works perfectly on my local machine, but at on the provider's machine, the output of calls to exec("php ...") seems to be always empty. I tried a call to ls and it gives me output, leading me to the assumption that PHP is somehow configured to not react to command line invocations or so. Does anyone know a way around this?
EDIT: I forgot to mention, I had already tried shell_exec and it gives no result, either. In response to sganesh's answer: I had tried that too, sorry I forgot to mention. However, the output (second argument) will always be an empty array, and the return value will always be 127, no matter if the PHP file to test has syntax errors or not.
I had the same problem. The solution that worked for me was found in running-at-from-php-gives-no-output. I needed to add output redirection.
$output = exec("php -l $filename 2>&1");
You can try with exec second and third arguments.
second argument will have the output of the command.
third argument will have the return value.
And exec will return only last line of the command.
$filename = "a.php";
$output = exec("php -l $filename",$op,$ret_val);
print $output."\n";
print $ret_val."\n";
var_dump($op);
By executing shell_exec(), you can see the output as if you executed that file via command line. You can just see if there is an error right here.
<?php
if (strpos(shell_exec('php -l file.php'), 'Syntax Error')) {
die('An error!');
}
There may also be a possibility that shell_exec() or exec() may be disable by your host.
Nice idea to check the file validity :-)!
Now, from the PHP manual for exec():
Note: When safe mode is enabled, you can only execute files within the safe_mode_exec_dir. For practical reasons, it is currently not allowed to have components in the path to the executable.
Can you check if this is not the case for you?
Also, can you check by providing the full path of the PHP interpreter in the exec() instead of only php. Let me know how you fare.
Pinaki
the correct way is to add >2&1 as tested on a windows system using imagemagick!
I worked around my original problem by using a different method. Here is what I do now:
Write a temporary file with contents <?php include "< File to test >"; echo "OK"; ?>
Generate the correct URL for the temporary file
Perform HTTP request with this URL
Check if result equals "OK". If yes, the file to test parses without errors.
Delete temporary file
Maybe this could be done without the temporary file by issuing an HTTP request to the file to test directly. However, if there is a parse error and errors are suppressed, the output will be empty and not discernible from the output in the case of a file that gives no parse errors. This method is risky because the file is actually executed instead of just checked. In my case, there is only a limited number of users who have access to this functionality in the first place. Still, I'm naturally not entirely happy with it.
Why the exec() approach did not work, I still do not know exactly. pinaki might be right by suggesting to provide the full path to the PHP executable, but I cannot find out the full path.
Thank you everyone for answering, I upvoted you all. However, I cannot accept any of your answers as none of your suggestions really solved my problem.
If a call to unlink() returns false for the specified path, how do you find out what the reason for the failure was (i.e. EISDIR, ENOENT, ELOOP etc.)? PHP 5.x running on redhat linux.
here's one way
unlink("/path/that/does/not/exist");
print_r(error_get_last());
See Error handling for more details
I don't think it is possible to get back any error code(s) issued by the system. That is maybe down to the fact that PHP is supposed to be portable, and different OS's have different methods of reporting errors.
You could of course do a exec('rm ....') and get the error level back but that's not very portable, and makes your app depend on exec() rights.
Otherwise, if you really, really need this, only a very hacky workaround comes to mind: Create a custom error handler function that tries to fetch the reason for the failure from the warning unlink throws - e.g. check for "Permission denied", or just fetch the whole error message.
Create a wrapper function around unlink that sets and re-sets the error handler. Something like this:
function my_unlink($file)
{
set_error_handler("my_error_handler");
unlink($file);
restore_error_handler();
}
you get my drift.
If anybody knows a better solution - I'd be interested to hear about it, too....
This is not possible, i'm afraid. Here's the C code that handles unlink in php 5.3.
ret = VCWD_UNLINK(url); <-- calls unlink(2)
if (ret == -1) {
if (options & REPORT_ERRORS) {
php_error_docref1(NULL TSRMLS_CC, url, E_WARNING, "%s", strerror(errno));
}
return 0;
}
as you can see, errno is not returned and there's no way to access it later.
There's already a bugreport about this, but it doesn't seem to draw too much attention. ;)
See also this discussion