Is it possible to debug this PHP - php

I'm really grasping at straws here. I don't code and my PHP email extension works great EXCEPT for these errors when editing the templates. My vendor is in who-knows what timezone and has been very unresponsive. Besides the obvious - change extensions, change vendors etc., can any of you PHP folk look at this code block and tell me what's wrong. I don't know if taken out of context it will be meaningful. The Errors are PHP Notice: Trying to get property of non-object beginning at line 1344. Here is a block of code from 1344 to 1370. It evidently has to do with switching to plain text when HTML isn't an option. AGAIN, grasping at straws here but stackoverflow is pretty solid when it comes to knowledgeable folk.
I'm re-pasting the code to include a more complete block. I have also added comments in the lines to indicate where the four warnings appear. They look like this:
//NEXT LINE IS ERROR ONE - LINE 1344 Notice: Trying to get property of non-object
Thank you
<?php
public static function isHTML($str) {
$html = array('A','ABBR','ACRONYM','ADDRESS','APPLET','AREA','B','BASE','BASEFONT','BDO','BIG','BLOCKQUOTE',
'BODY','BR','BUTTON','CAPTION','CENTER','CITE','CODE','COL','COLGROUP','DD','DEL','DFN','DIR','DIV','DL',
'DT','EM','FIELDSET','FONT','FORM','FRAME','FRAMESET','H1','H2','H3','H4','H5','H6','HEAD','HR','HTML',
'I','IFRAME','IMG','INPUT','INS','ISINDEX','KBD','LABEL','LEGEND','LI','LINK','MAP','MENU','META',
'NOFRAMES','NOSCRIPT','OBJECT','OL','OPTGROUP','OPTION','P','PARAM','PRE','Q','S','SAMP','SCRIPT',
'SELECT','SMALL','SPAN','STRIKE','STRONG','STYLE','SUB','SUP','TABLE','TBODY','TD','TEXTAREA','TFOOT',
'TH','THEAD','TITLE','TR','TT','U','UL','VAR');
return preg_match("~(<\/?)\b(".implode('|', $html).")\b([^>]*>)~i", $str, $c);
}
private function _html_to_plain_text($node) {
if ($node instanceof DOMText) {
return preg_replace("/\\s+/im", " ", $node->wholeText);
}
if ($node instanceof DOMDocumentType) {
// ignore
return "";
}
// Next
//NEXT LINE IS ERROR ONE - LINE 1344 Notice: Trying to get property of non-object
$nextNode = $node->nextSibling;
while ($nextNode != null) {
if ($nextNode instanceof DOMElement) {
break;
}
$nextNode = $nextNode->nextSibling;
}
$nextName = null;
if ($nextNode instanceof DOMElement && $nextNode != null) {
$nextName = strtolower($nextNode->nodeName);
}
// Previous
//NEXT LINE IS ERROR TWO - LINE 1357 Notice: Trying to get property of non-object
$nextNode = $node->previousSibling;
while ($nextNode != null) {
if ($nextNode instanceof DOMElement) {
break;
}
$nextNode = $nextNode->previousSibling;
}
$prevName = null;
if ($nextNode instanceof DOMElement && $nextNode != null) {
$prevName = strtolower($nextNode->nodeName);
}
//NEXT LINE IS ERROR THREE - LINE 1369 Notice: Trying to get property of non-object
$name = strtolower($node->nodeName);
// start whitespace
switch ($name) {
case "hr":
return "------\n";
case "style":
case "head":
case "title":
case "meta":
case "script":
// ignore these tags
return "";
case "h1":
case "h2":
case "h3":
case "h4":
case "h5":
case "h6":
// add two newlines
$output = "\n";
break;
case "p":
case "div":
// add one line
$output = "\n";
break;
default:
// print out contents of unknown tags
$output = "";
break;
}
// debug $output .= "[$name,$nextName]";
//NEXT LINE IS ERROR FOUR - LINE 1408 Notice: Trying to get property of non-object
if($node->childNodes){
for ($i = 0; $i < $node->childNodes->length; $i++) {
$n = $node->childNodes->item($i);
$text = $this->_html_to_plain_text($n);
$output .= $text;
}
}
// end whitespace
switch ($name) {
case "style":
case "head":
case "title":
case "meta":
case "script":
// ignore these tags
return "";
case "h1":
case "h2":
case "h3":
case "h4":
case "h5":
case "h6":
$output .= "\n";
break;
case "p":
case "br":
// add one line
if ($nextName != "div")
$output .= "\n";
break;
case "div":
// add one line only if the next child isn't a div
if (($nextName != "div" && $nextName != null) || ($node->hasAttribute('class') && strstr($node->getAttribute('class'), 'emailtemplateSpacing')))
$output .= "\n";
break;
case "a":
// links are returned in [text](link) format
$href = $node->getAttribute("href");
if ($href == null) {
// it doesn't link anywhere
if ($node->getAttribute("name") != null) {
$output = "$output";
}
} else {
if ($href == $output || ($node->hasAttribute('class') && strstr($node->getAttribute('class'), 'emailtemplateNoDisplay'))) {
// link to the same address: just use link
$output;
} else {
// No display
$output = $href . "\n" . $output;
}
}
// does the next node require additional whitespace?
switch ($nextName) {
case "h1": case "h2": case "h3": case "h4": case "h5": case "h6":
$output .= "\n";
break;
}
default:
// do nothing
}
return $output;
}
}
?>

Sounds like the $node variable coming into your function from private function _html_to_plain_text($node) isn't the correct variable type. Might be an int, string, array, or null, instead of whatever object/class it's supposed to be. You probably need to look at whatever code is calling that function, rather than the function itself.
Debugging in PHP is a skill in itself.
If coding by hand, consider inserting var_export($node); statements before and after areas where you suspect there is a problem so you can examine it.
If you have access to a debugger (for example, Eclipse + XAMPP + XDebug), use that and go line by line, examining the contents of the $node variable by hovering over it or looking at your variable list.

Related

break if variable has specific value

I try to listen for the value of a variable and execute some code if it has a specific value.
e.g. I execute the function element() inside a switch statement and I have to execute break if the value of $element is false.
At the moment I do it like this:
switch(strtolower($testcase)) {
case 'test':
$element = $sel->element("class name", "btn-primary", true);
if ($element == false) { break; }
$element->click("");
...
public function element($using, $value, $takeScreenshot=false, $customMessage="")
{
try {
$element = $this->driver->element($using, $value);
} catch(PHPWebDriver_UnhandledWebDriverError $exception) {
$msg = "<b>PHPWebDriver_UnhandledWebDriverError: $using</b> is "
. "not a valid value for the parameter <b>\$using</b>!";
if ($customMessage !== "") {
$msg = "<b>$customMessage</b><br>$msg";
}
$this->setMessage(1, $msg, $exception, $takeScreenshot, $value);
return false;
} catch(PHPWebDriver_NoSuchElementWebDriverError $exception) {
$msg = "<b>PHPWebDriver_NoSuchElementWebDriverError:</b> No "
. "Element with <b>'$using'</b> and value <b>'$value'</b> found !";
if ($customMessage !== "") {
$msg = "<b>$customMessage</b><br>$msg";
}
$this->setMessage(1, $msg, $exception, $takeScreenshot, $value);
return false;
}
return $element;
}
But I would like to simplify it so that the line if ($element == false) { break; } is not needed anymore. e.g.
switch(strtolower($testcase)) {
case 'test':
$element = $this->element("class name", "btn-primary", true);
$element->click("");
...
I tried to return break; but this failed. Is there another way? e.g. can I add an EventListener which executes break automatically in the switch case if the value of $element is false?
When building functions, it's usually best to design them to be de-coupled, not coupled. This means, element() shouldn't care about what is calling it.
Therefore, attempting to break a switch from within element() would be a bad programming design.
If element() should always stop execution when it hits a certain spot, regardless of what called it, you should consider throwing an exception. However, this won't simplify the code in your switch statement as you'd have to use try, catch blocks.
With that said, since PHP allows assignments in conditionals, you could simplify the code you have written as:
if ($element = $sel->element("class name", "btn-primary", true)) {
$element->click("");
}

How can I incporporate a Geolocation lookup into a switch?

In my Wordpress theme I have a function that looks a bit like this:
function variable($value) {
switch ($value) {
case 'prem_no':
$prem_no_uk = '0906 636 4355';
$prem_no_aus = '';
$prem_no_us = '';
return $prem_no;
break;
}
}
On the page I am calling:
echo variable('prem_no');
I want to use this script to get the users location, and depending on the returning location, return one of the three variables listed in the case above.
http://www.hostip.info/use.html
the problem I have, is I've looked on that site, and it just gives a URL. I don't know how to write an IF statement, or whatever is needed.
I think logically, I want to only have one call for the main location finder, then store that value as a variable elsewhere on the site.
Then in the return for the cases add a suffix at the end of prem_no that would match the cases above.
Is that along the right tracks?
UPDATE:
Right I'll try and have a go in a slightly different direction.
At the top of the header file I've got this:
require_once 'geoip.inc';
$gi = geoip_open('GeoIP.dat',GEOIP_STANDARD);
$country_code = geoip_country_code_by_addr($gi, $_SERVER['REMOTE_ADDR']);
geoip_close($gi);
I'm getting a error message saying:
Warning: fopen(GeoIP.dat): failed to open stream: No such file or directory in C:\wamp\www\clairvoyant\wp-content\themes\clairvoyant\geoip.inc on line 314
What I don't get is that the path is absolutely correct, the file is there. So why can't it find it?
Okay, so I've managed to partly fix my issues here.
I put the switch and the code into a single function like this:
function variable($value) {
$country_code = '';
require_once("geoip.inc");
$gi = geoip_open(dirname(__FILE__)."/GeoIP.dat",GEOIP_STANDARD);
$country_code = geoip_country_code_by_addr($gi, $_SERVER['REMOTE_ADDR']);
geoip_close($gi);
switch ($value) {
case 'prem_no':
if ($country_code == 'US') { $prem_no = '888-888-8888'; }
elseif ($country_code == 'AU') { $prem_no = '1900 000 000'; }
else { $prem_no = '0906 636 4355'; }
return $prem_no;
break;
case 'prem_rate':
if ($country_code == 'US') { $prem_rate = '$3.50'; }
elseif ($country_code == 'AUS') { $prem_rate = '$3.96'; }
else { $prem_rate = '£1.53'; }
return $prem_rate;
break;
case 'local_no':
if ($country_code == 'US') { $local_no = '755-555-5555'; }
elseif ($country_code == 'AUS') { $local_no = '1800 000 000'; }
else { $local_no = '0207 111 6311'; }
return $local_no;
break;
case 'sms_no':
if($country_code == 'AUS') { $sms_no = '1977 1977'; }
else { $sms_no = '78887'; }
return $sms_no;
break;
case 'sms_rate':
if($country_code == 'AUS') { $sms_rate = '25c'; }
else { $sms_rate = '£1.50'; }
return $sms_rate;
break;
case 'helpline':
if($country_code == 'US') { $helpline = '700-777-7777'; }
elseif ($country_code == 'AUS') { $helpline = '1700 000 000'; }
else { $helpline = '0207 111 6210'; }
return $helpline;
break;
default:
break;
}
}
The file GeoIP.dat loads perfectly fine on every normal page, except when looking at the blog pages. (I'm assuming it must read functions differently or something? The error I get when trying to view the Blog loop articles is:
Warning: fopen(GeoIP.dat): failed to open stream: No such file or directory in C:\wamp\www\clairvoyant\wp-content\themes\clairvoyant\geoip.inc on line 314
then at the bottom of the trace table it says
Can not open GeoIP.dat
I just can't work out why it's working fine for normal pages, but not for blogs. Can anyone shed any light?
UPDATE:
It turns out Wordpress uses different pages for blog and normal pages. So I just needed to edit the other pages to match the initial ones, and it all seems to be working now.
Thanks for all of those who took the time to read my questions, sorry if it wasn't too clear or logical. But thanks anyway. All is good!

Calling PHP functions through another file

I created a simple sample php file for display some function outputs. here is the code..
<?php
// $printName = hello('samitha');
// $printHeader = pageHeader('main','on');
switch (key($_GET)){
case 'red':
$printHeader = pageHeader('red','on');
$printName = hello('Joel');
break;
case 'blue':
$printHeader = pageHeader('blue','off');
$printName = hello('Duck');
break;
case 'yellow':
//$printHeader = pageHeader('yellow','on');
break;
}
function hello($name){
return $name;
}
function pageHeader($header,$onoff){
if ($onoff == 'on') {
return $header."page header<br>";
}
else {return null;}
}
echo $printHeader;
echo $printName;
?>
This code is working fine without any problems.
When I call 'example.com/php/tipo34.php?red', it shows on the screen:
redpage header
Joel
And when I call 'example.com/php/tipo34.php?blue' it shows on the screen:
Duck
I tried to put the below functions inside of another php file called tipo34-req.php and received the following error:
Fatal error: Call to undefined function pageHeader() in C:\wamp\www\php\tipo34.php on line 8
The code I tried:
<?php
// $printName = hello('samitha');
// $printHeader = pageHeader('main','on');
switch (key($_GET)){
case 'red':
$printHeader = pageHeader('red','on');
$printName = hello('samitha');
break;
case 'blue':
$printHeader = pageHeader('blue','off');
$printName = hello('kalum');
break;
case 'yellow':
//$printHeader = pageHeader('yellow','on');
break;
}
include 'tipo34-req.php';
echo $printHeader;
echo $printName;
?>
tipo34-req.php code:
<?php
function hello($name){
global $name;
return $name;
}
function pageHeader($header,$onoff){ global $header, $onoff
if ($onoff == 'on') {
return $header."page header<br>";
}
else {return null;}
}
?>
How do I solve this problem? Using the function directly on the file works, but when I put the functions in another php file, it throws the error.
Thanks.
Include your file above its contents usage. PHP is unaware of the functions since they are included later in the code.
include 'tipo34-req.php';
switch (key($_GET)){
case 'red':
$printHeader = pageHeader('red','on');
$printName = hello('samitha');
break;
case 'blue':
$printHeader = pageHeader('blue','off');
$printName = hello('kalum');
break;
case 'yellow':
//$printHeader = pageHeader('yellow','on');
break;
}
echo $printHeader;
echo $printName;
?>
Have you tried including the file at the top before calling any of the functions?

Removing allowable whitespace in PHP using parser tokens

I am trying to make a simple script that will remove all unnecessary whitespace from a PHP file/string.
I have been successful at parsing the string using tokens, but don't see a good method to remove "extra" whitespace.
For instance,
function test() { return TRUE; }
should be
function test(){return TRUE;}
and NOT
functiontest(){returnTRUE;}
You end up with the last version if you just remove the T_WHITESPACE token.
Is there something I am missing to remove whitespace, but keep spaces after things like "function " and "return ". Thank you!
$newSource = '';
foreach (token_get_all($source) as $i => $token) {
if (!is_array($token)) {
$newSource .= $token;
}
if ($token[0] == T_WHITESPACE) {
if ( isset($tokens[$i - 1]) && isset($tokens[$i + 1])
&& is_array($tokens[$i - 1]) && is_array($tokens[$i + 1])
&& isLabel($tokens[$i - 1][1]) && isLabel($tokens[$i + 1][1])
) {
$newSource .= ' ';
}
} else {
$newSource .= $token[1];
}
}
function isLabel($str) {
return preg_match('~^[a-zA-Z0-9_\x7f-\xff]+$~', $str);
}
Removing the whitespace is always allowed, apart from the case where there is a LABEL on both sides of it. I check that and either add nothing or a single space character.
There is just another special case I know about, there whitespace is of importance: T_END_HEREDOC must be followed by either ; or \n. Compacting or stripping the space here is not allowed. So, if that is of importance to you, you can simply add that ;)
Well, T_WHITESPACE can be spaces or newlines, etc. So one trivial approach would be to automatically replace all T_WHITESPACE instances with a new one that consists of exactly one space.
But for a smarter method, just go through the list of parser tokens, and figure out which ones should have a whitespace after it and which should not (something like this):
foreach ($tokens as $k => $val) {
if (is_array($val) && $val[0] == T_WHITESPACE) {
if (!is_array($tokens[$k - 1])) {
//remove this space
} else {
switch ($tokens[$k - 1][0]) {
case T_ABSTRACT:
case T_FUNCTION:
//.. other keeps here:
continue;
break;
default:
//remove the space
}
}
}
}
And one more note, don't do this for performance. If you're using an OPCODE Cache (Such as APC) you'll see no benefit for a lot of work. If you're not using one, why aren't you?
Your effort is futile.
php -w
Allows already to strip whitespace off scripts. It uses a bit more elaborate logic to remove whitespace from the token stream.
Here's the zend_strip() function as found in zend_highlight.c:
while ((token_type=lex_scan(&token TSRMLS_CC))) {
switch (token_type) {
case T_WHITESPACE:
if (!prev_space) {
zend_write(" ", sizeof(" ") - 1);
prev_space = 1;
}
/* lack of break; is intentional */
case T_COMMENT:
case T_DOC_COMMENT:
token.type = 0;
continue;
case T_END_HEREDOC:
zend_write(LANG_SCNG(yy_text), LANG_SCNG(yy_leng));
efree(token.value.str.val);
/* read the following character, either newline or ; */
if (lex_scan(&token TSRMLS_CC) != T_WHITESPACE) {
zend_write(LANG_SCNG(yy_text), LANG_SCNG(yy_leng));
}
zend_write("\n", sizeof("\n") - 1);
prev_space = 1;
token.type = 0;
continue;
default:
zend_write(LANG_SCNG(yy_text), LANG_SCNG(yy_leng));
break;
}
if (token.type == IS_STRING) {
switch (token_type) {
case T_OPEN_TAG:
case T_OPEN_TAG_WITH_ECHO:
case T_CLOSE_TAG:
case T_WHITESPACE:
case T_COMMENT:
case T_DOC_COMMENT:
break;
default:
efree(token.value.str.val);
break;
}
}
prev_space = token.type = 0;
}

How to clean and simplify this code?

After thinking about This Question and giving an answer to it I wanted to do more about that to train myself.
So I wrote a function which will calc the length of an given function. Th given php-file has to start at the beginning of the needed function.
Example: If the function is in a big phpfile with lots of functions, like
/* lots of functions */
function f_interesting($arg) {
/* function */
}
/* lots of other functions */
then $part3 of my function will require to begin like that (after the starting-{ of the interesting function):
/* function */
}
/* lots of other functions */
Now that's not the problem, but I would like to know if there are an cleaner or simplier ways to do this. Here's my function: (I already cleaned a lot of testing-echo-commands)
(The idea behind it is explained here)
function f_analysis ($part3) {
if(isset($part3)) {
$char_array = str_split($part3); //get array of chars
$end_key = false; //length of function
$depth = 0; //How much of unclosed '{'
$in_sstr = false; //is next char inside in ''-String?
$in_dstr = false; //is nect char inside an ""-String?
$in_sl_comment = false; //inside an //-comment?
$in_ml_comment = false; //inside an /* */-comment?
$may_comment = false; //was the last char an '/' which can start a comment?
$may_ml_comment_end = false; //was the last char an '*' which may end a /**/-comment?
foreach($char_array as $key=>$char) {
if($in_sstr) {
if ($char == "'") {
$in_sstr = false;
}
}
else if($in_dstr) {
if($char == '"') {
$in_dstr = false;
}
}
else if($in_sl_comment) {
if($char == "\n") {
$in_sl_comment = false;
}
}
else if($in_ml_comment) {
if($may_ml_comment_end) {
$may_ml_comment_end = false;
if($char == '/') {
$in_ml_comment = false;
}
}
if($char == '*') {
$may_ml_comment_end = true;
}
}
else if ($may_comment) {
if($char == '/') {
$in_sl_comment = true;
}
else if($char == '*') {
$in_ml_comment = true;
}
$may_comment = false;
}
else {
switch ($char) {
case '{':
$depth++;
break;
case '}':
$depth--;
break;
case '/':
$may_comment = true;
break;
case '"':
$in_dstr = true;
break;
case "'":
$in_sstr = true;
break;
}
}
if($depth < 0) {
$last_key = $key;
break;
}
}
} else echo '<br>$part3 of f_analysis not set!';
return ($last_key===false) ? false : $last_key+1; //will be false or the length of the function
}
Tokenizer (Example) - Learn it, love it.
You could probably reduce the number of state variables a little, but truthfully... yes, it will be messy code. I would probably get rid of $may_ml_comment_end and peek ahead for the next character when I encounter an asterisk, for example. You will need to rewrite your foreach loop to a regular for loop be able to do that without creating a bigger mess though.
PS: I don't see you handling the escape character yet. Without the above approach, that would introduce another boolean variable.
Another problem with your current code is that characters immediately following a / don't get interpreted as they should. However unlikely
echo 5/'2'; // NB: no space in between
is valid in PHP and would break your parser.

Categories