Throwing errors from its "correct" source - php

I hope the title isn't too confusing, I'll try to explain better below.
Suppose I have a function in a separate file, functions.php:
function divide($num1, $num2) {
if ($num1 == 0 || $num2 == 0) {
trigger_error("Cannot divide by 0", E_USER_ERROR);
} else {
return ($num1 / $num2);
}
}
And another file that calls it:
include "functions.php";
echo divide(10, 0);
My error is
Fatal error: Cannot divide by 0 in
C:\Users\Derek\Desktop\projects\functions.php on line 5
My question is, how do I make that error instead point to the location of the error in the main code, so I instead get:
Fatal error: Cannot divide by 0 in
C:\Users\Derek\Desktop\projects\main.php on line 3
The particular reason I want this is because I have a function called load_class that simply finds a PHP file and instantiates the object inside, but if given an incorrect file name, it reports an error from inside load_class, which is technically true, but it's not particularly helpful if I don't remember where I called load_class in the first place. I would like the error to point to the file that called load_class incorrectly.
Also, I would like to write a function error() (something like below) that when given a message as a parameter would throw more "meaningful" error messages, but when done that way, the error always says it comes from error(), not from where the error actually came from!
For example, in an error.php:
/**
* error()
*
* Throws an error of a certain type
*
* #param string $type The type of error. "Fatal", "warning," or "notice"
* #param string $message A description of the error
* #return void
*/
function error($type, $message) {
switch (strtolower($type)) {
case 'fatal':
trigger_error($message, E_USER_ERROR);
break;
case 'notice':
trigger_error($message, E_USER_NOTICE);
default:
trigger_error($message, E_USER_WARNING);
break;
}
}
And in an index.php
error("fatal", "A sample warning!");
My error given is:
Fatal error: A sample warning! in
C:\Users\Derek\Desktop\projects\synthesis\sys\Error.php on line 45
But the error didn't occur in error.php, it happened in index.php! How can I make it show where it really came from?

The debug_backtrace function allows you to obtain the stacktrace as an array. You can pick the original location from there.
Next to that you need to slip into the error message to make this look-alike. Example:
function divide($num1, $num2) {
if ($num1 == 0 || $num2 == 0) {
trigger_error_original("Cannot divide by 0", E_USER_ERROR);
} else {
return ($num1 / $num2);
}
}
function trigger_error_original($message, $type) {
$trace = debug_backtrace(FALSE);
list($location) = array_slice($trace, 1, 1) + array('file' => 'unknown', 'line' => 'unknown');
$message .= sprintf(" in %s on line %d\nTriggered", $location['file'], $location['line']);
trigger_error($message, $type);
}
divide(1, 0);
The error message than shows something like:
> php test-pad.php
Fatal error: Cannot divide by 0 in test-pad.php on line 18
Triggered in test-pad.php on line 15
The downside of this is, that you need to change your code to have this "feature". If you need this for debugging your own code, it's much better that you enable backtraces in your logs. The Xdebug extension does this for you, or you can write your own error handler that takes care of that.
See as well the related question Caller function in PHP 5?. I used array_slice so that you could create an additional parameter to define the number of steps you want to go "up" in the backtrace.

Use debug_backtrace(), and debug_print_backtrace() for a full call stack. These are especially effective when using Xdebug, which will override the function to colorize the output.

I have this same problem...
#1: while 10/0 = ERROR, 0/10 = 0 is perfectly legal, you shouldn't have an exception for that.
#2: when you include a file, it effectively becomes part of this new file, so perhaps you might have to toy a little bit with things like __FILE__ and see if you can make it point it to the file before it gets included in the other file..

You can use xdebug - it will show you the stacktrace or you can register your own error handndler and display the stacktrace. Just check the example in php.net for set_error_handler().

Maybe exceptions are better to use in your case. You get the full stacktrace and can locate where the function was called without relying on some tricky code :)

Related

PHP8 - Fatal User Error: Unsupported operand types: array & int

I am trying to fix some PHP8 errors in forum software, need assistance with this.
Error thrown is Fatal User Error: Unsupported operand types: array & int on these lines.
$forumperms & $this->registry->bf_ugp_forumpermissions['cangetattachment'],
$forumperms & $this->registry->bf_ugp_forumpermissions['canseethumbnails']
For reference the whole code section below.
/*
* Processes any attachments to this post.
*/
function process_attachments()
{
global $show;
$forumperms = fetch_permissions($this->thread['forumid']);
require_once(DIR . '/packages/vbattach/attach.php');
$attach = new vB_Attach_Display_Content($this->registry, 'vBForum_Post');
if ($this->post['allattachments'])
{
foreach($this->post['allattachments'] AS $attachmentid => $info)
{
if (!$this->post['attachments'][$attachmentid])
{
unset($this->post['allattachments'][$attachmentid]);
}
}
$attach->process_attachments(
$this->post,
$this->post['allattachments'],
(THIS_SCRIPT == 'external'),
can_moderate($this->forum['forumid'], 'canmoderateattachments'),
$forumperms & $this->registry->bf_ugp_forumpermissions['cangetattachment'], //ERROR
$forumperms & $this->registry->bf_ugp_forumpermissions['canseethumbnails'] //ERROR
);
}
else
{
$show['attachments'] = $show['moderatedattachment'] = $show['thumbnailattachment'] = $show['otherattachment'] = false;
$show['imageattachment'] = $show['imageattachmentlink'] = false;
}
}
Have not tried anything yet trying to work out how those lines must be changed for PHP8 compatability.
Unsupported operand type errors occurred when some kind of mathematical calculation was involved in some part of the code and one of the operands could not be automatically converted to the other type. In your case, the calculation seems to have one operand as int and the other one being an array and an array could not be automatically converted to int, hence the issue.
In your code, the 2 lines that you have marked as the error-causing lines are not the actual point of error. Starting from PHP 8 strict typings have been introduced almost everywhere in PHP core functions so legacy code will break. You can read more here
I think you need to check for vB_Attach_Display_Content::process_attachments method for any mathematical calculations. In the given code block you are passing different parameters to that function and maybe those parameters are not strictly typed. Hence the function is accepting anything.

Return in function not working

I am subscribing to data from a MQTT broker with phpMQTT. I have successfully set up a pub / sub routine based on their basic implementation. I can echo the information just fine inside the procmsg() function.
However, I need to take the data I receive and use it for running a few database operations and such. I can't seem to get access to the topic or msg received outside of the procmsg() function. Using return as below seems to yield nothing.
<?php
function procmsg($topic, $msg){
$value = $msg * 10;
return $value;
}
echo procmsg($topic, $msg);
echo $value;
?>
Obviously I am doing something wrong - but how do I get at the values so I can use them outside the procmsg()? Thanks a lot.
I dont know about that lib, but in that code
https://github.com/bluerhinos/phpMQTT/blob/master/phpMQTT.php ,
its possible see how works.
in :
$topics['edafdff398fb22847a2f98a15ca3186e/#'] = array("qos"=>0, "function"=>"procmsg");
you are telling it that topic "edafdff398fb22847a2f98a15ca3186e/#" will have Quality of Service (qos) = 0, and an "event" called 'procmsg'.
That's why you later wrote this
function procmsg($topic,$msg){ ... }
so in the while($mqtt->proc()) this function will check everytime if has a new message (line 332 calls a message function and then that make a call to procmsg of Source Code)
thats are the reason why you cannot call in your code to procmsg
in other words maybe inside the procmsg you can call the functions to process message ej :
function procmsg($topic,$msg){
$value = $msg * 10;
doStuffWithDataAndDatabase($value);
}
Note that you can change the name of the function simply ej :
$topics['edafdff398fb22847a2f98a15ca3186e/#'] = array("qos"=>0, "function"=>"onMessage");
and then :
function onMessage($topic,$msg){
$value = $msg * 10;
doStuffWithDataAndDatabase($value);
}
Sorry for my english, hope this help !

Catchable fatal error: Argument 1 passed to CorenlpAdapter::getOutput()

I get the following error:
Catchable fatal error: Argument 1 passed to CorenlpAdapter::getOutput() must be an instance of string, string given, called in /Library/WebServer/Documents/website/php-stanford-corenlp-adapter/index.php on line 22 and defined in /Library/WebServer/Documents/website/php-stanford-corenlp-adapter/src/CoreNLP/CorenlpAdapter.php on line 95
index.php 21 and 22 contain:
$text1 = 'I will meet Mary in New York at 10pm';
$coreNLP->getOutput($text1);
corenlpAdapter.php lines 95 and onwards contain:
public function getOutput(string $text){
if(ONLINE_API){
// run the text through the public API
$this->getServerOutputOnline($text);
} else{
// run the text through Java CoreNLP
$this->getServerOutput($text);
}
// cache result
$this->serverMemory[] = $this->serverOutput;
if(empty($this->serverOutput)){
echo '** ERROR: No output from the CoreNLP Server **<br />
- Check if the CoreNLP server is running. Start the CoreNLP server if necessary<br />
- Check if the port you are using (probably port 9000) is not blocked by another program<br />';
die;
}
/**
* create trees
*/
$sentences = $this->serverOutput['sentences'];
foreach($this->serverOutput['sentences'] as $sentence){
$tree = $this->getTreeWithTokens($sentence); // gets one tree
$this->trees[] = $tree; // collect all trees
}
/**
* add OpenIE data
*/
$this->addOpenIE();
// to get the trees just call $coreNLP->trees in the main program
return;
}
Why exactly am I getting this error when text1 is a string?
I am the original author of this class. As you can see, the function getOutput looks like this:
public function getOutput(string $text){
...
}
Change that to:
public function getOutput($text){
...
}
The function tries to enforce that the input is string. The original code should work. However, it seems that in your case, PHP thinks "string" is not actually a string. Maybe the coding environment (the IDE) you are using uses the wrong character set? Or maybe you copy-pasted the code from HTML into the IDE or something like that. Thus whilst it says "string" on the screen, it's not actually a string to PHP.
If you are sure the input is a string, you can safely change the code like above. The class should then work normally.
public function getOutput($text){
.
.
.
}

CakePHP Notice (8) raised : Use of undefined constant inList - assumed 'inList'

Notice (8): Use of undefined constant inList - assumed 'inList' [CORE\Cake\Utility\ClassRegistry.php, line 168]
This notice has been bugging me for a while know, and I do not know how to fix it.. It was not really affecting my project earlier since its just a notice msg, but now, it is not letting me show an error message which I am trying to display to the user.
Iv got this function
public function validate_form(){
if($this->RequestHandler->isAjax()){
$this->request->data['Donor'][$this->params['form']['field']] = $this->params['form']['value'];
$this->Donor->set($this->data);
if($this->Donor->validates()){
$this->autoRender = FALSE;
}else{
$error = $this->Donor->validationErrors;
$this->set('error',$error[$this->params['form']['field']]);
}
}
}
The above is the action to which my post request submits to. Then it executes the following to display the error
if (error.length > 0) {
if ($('#name-notEmpty').length == 0) {
$('#DonorName').after('<div id="name-notEmpty" class="error-message">' + error + '</div>');
}
}else{
$('#name-notEmpty').remove();
}
The problem is that instead of the relevant error in my newly created div... I get that notice 8 from cake! Please if anyone knows why this is happening, I appreciate your aid on this one..
TLDR:
Do a project-wide find for 'inList' and find the spot where it either doesn't have quotes around it, or, if it's supposed to be a variable, is missing it's $.
Explanation:
You get that error when you try to use a PHP Constant that doesn't exist. Usually you're not actually TRYING to use a constant, but instead just forgot to wrap quotes around something or forgot to add the $ before a variable.
Examples:
$name = "Dave";
echo name; // <-- WOAH THERE, there is no Constant called name (missing $)
$people = array('Dave' => 'loves pizza');
echo $people[Dave]; // <-- WOAH THERE, no Constant called Dave (missing quotes)
Most likely somewhere else in your code you are using 'inList' as an array key but you don't have it quoted.
Example: $value = $myArray[inList];
It still works without quoting inList but it causes the notice message you're seeing.

In PHP, how can I detect that input vars were truncated due to max_input_vars being exceeded?

I know that an E_WARNING is generated by PHP
PHP Warning: Unknown: Input variables exceeded 1000
But how can I detect this in my script?
A "close enough" method would be to check if( count($_POST, COUNT_RECURSIVE) == ini_get("max_input_vars"))
This will cause a false positive if the number of POST vars happens to be exactly on the limit, but considering the default limit is 1000 it's unlikely to ever be a concern.
count($_POST, COUNT_RECURSIVE) is not accurate because it counts all nodes in the array tree whereas input_vars are only the terminal nodes. For example, $_POST['a']['b'] = 'c' has 1 input_var but using COUNT_RECURSIVE will return 3.
php://input cannot be used with enctype="multipart/form-data". http://php.net/manual/en/wrappers.php.php
Since this issue only arises with PHP >= 5.3.9, we can use anonymous functions. The following recursively counts the terminals in an array.
function count_terminals($a) {
return is_array($a)
? array_reduce($a, function($carry, $item) {return $carry + count_terminals($item);}, 0)
: 1;
}
What works for me is this. Firstly, I put this at the top of my script/handler/front controller. This is where the error will be saved (or $e0 will be null, which is OK).
$e0 = error_get_last();
Then I run a bunch of other processing, bootstrapping my application, registering plugins, establishing sessions, checking database state - lots of things - that I can accomplish regardless of exceeding this condition.. Then I check this $e0 state. If it's not null, we have an error so I bail out (assume that App is a big class with lots of your magic in it)
if (null != $e0) {
ob_end_clean(); // Purge the outputted Warning
App::bail($e0); // Spew the warning in a friendly way
}
Tweak and tune error handlers for your own state.
Registering an error handler won't catch this condition because it exists before your error handler is registered.
Checking input var count to equal the maximum is not reliable.
The above $e0 will be an array, with type => 8, and line => 0; the message will explicitly mention input_vars so you could regex match to create a very narrow condition and ensure positive identification of the specific case.
Also note, according to the PHP specs this is a Warning not an Error.
function checkMaxInputVars()
{
$max_input_vars = ini_get('max_input_vars');
# Value of the configuration option as a string, or an empty string for null values, or FALSE if the configuration option doesn't exist
if($max_input_vars == FALSE)
return FALSE;
$php_input = substr_count(file_get_contents('php://input'), '&');
$post = count($_POST, COUNT_RECURSIVE);
echo $php_input, $post, $max_input_vars;
return $php_input > $post;
}
echo checkMaxInputVars() ? 'POST has been truncated.': 'POST is not truncated.';
Call error_get_last() as soon as possible in your script (before you have a chance to cause errors, as they will obscure this one.) In my testing, the max_input_vars warning will be there if applicable.
Here is my test script with max_input_vars set to 100:
<?php
if (($error = error_get_last()) !== null) {
echo 'got error:';
var_dump($error);
return;
}
unset($error);
if (isset($_POST['0'])) {
echo 'Got ',count($_POST),' vars';
return;
}
?>
<form method="post">
<?php
for ($i = 0; $i < 200; $i++) {
echo '<input name="',$i,'" value="foo" type="hidden">';
}
?>
<input type="submit">
</form>
Output when var limit is hit:
got error:
array
'type' => int 2
'message' => string 'Unknown: Input variables exceeded 100. To increase the limit change max_input_vars in php.ini.' (length=94)
'file' => string 'Unknown' (length=7)
'line' => int 0
Tested on Ubuntu with PHP 5.3.10 and Apache 2.2.22.
I would be hesitant to check explicitly for this error string, for stability (they could change it) and general PHP good practice. I prefer to turn all PHP errors into exceptions, like this (separate subclasses may be overkill, but I like this example because it allows # error suppression.) It would be a little different coming from error_get_last() but should be pretty easy to adapt.
I don't know if there are other pre-execution errors that could get caught by this method.
What about something like that:
$num_vars = count( explode( '###', http_build_query($array, '', '###') ) );
You can repeat it both for $_POST, $_GET, $_COOKIE, whatever.
Still cant be considered 100% accurate, but I guess it get pretty close to it.

Categories