ExtJS line Charts and improper string data from PHP

While working on the main dashboard for the current extjs project I am working I had to create a line chart which showed the performance over the last 1 week. This was across 3 different variables.

The code to show that with tips and all as an item in an ExtJS Panel is

new Ext.chart.LineChart({
                    store: new Ext.data.JsonStore({
                        url:PHP_AJAX_URL + '/hometab.php',
                        baseParams:{
                            task:'graphdata'
                        },
                        idProperty: 'gdate',
                        root:'data',
                        fields: ['gdate', 'f1', 'f2','f3'],
                        autoLoad: true
                    }),
                    xField:'gdate',
                    series:[
                    {
                        type:'line',
                        displayName: 'FONE',
                        yField: 'f1',
                        style: {
                            color: 0x889fbb
                        }
                    }, {
                        type: 'line',
                        displayName: 'FTWO',
                        yField: 'f2',
                        style: {
                            color: 0x889fcc
                        }
                    }, {
                        type:'line',
                        displayName: 'FTHREE',
                        yField: 'f3',
                        style: {
                            color: 0x6666bb
                        }
                    }
                    ],
                    tipRenderer: function (chart, record, index, series){
                        if (series.yField=='f1')
                            return Ext.util.Format.number(record.data.f1, '0,0') +
                            ' f1 on ' + record.data.gdate;
                        if (series.yField=='f2')
                            return Ext.util.Format.number(record.data.f2, '0,0')+
                            ' f2 on ' + record.data.gdate;
                        if (series.yField=='f3')
                            return Ext.util.Format.number(record.data.f3, '0,0') + 
                            ' f3 on ' + record.data.gdate;

                    },
                    extraStyle: {
                        padding: 10,
                        animationEnabled: true,
                        legend:{
                            display:'bottom'
                        },
                        xAxis: {
                            color: 0x3366cc,
                            majorGridLines: {
                                size: 0,
                                color: 0xdddddd
                            }
                        },
                        yAxis: {
                            color: 0x3366cc,
                            majorTicks: {
                                color: 0x3366cc,
                                length: 0
                            },
                            minorTicks: {
                                color: 0x3366cc,
                                length: 0
                            },
                            majorGridLines: {
                                size: 0,
                                color: 0xdddddd
                            }
                        }
                    }
                })

Like all other parts of the backend the code was being pulled via a quick mysql_query from the database and provided to the ExtJS frontend via JSON.

        $graph_query="select 
				date(f_date) as gdate,
                sum(f1) as f1,
                sum(f2) as f2,
                sum(f3) as f3
                from stats
				where id=user
                and date(f_date)>date_sub(current_date, interval 7 day)
                group by date(f_date)";
        $graph_recordset=mysql_query($graph_query);
		while($graph_row=mysql_fetch_assoc($graph_recordset)){
			$response['data']=$graph_row;
		}
		$response['success']=true;
		print json_encode($response);

However, the LineChart wasn’t rendering properly and it was showing wierd values for some and not rendering at all for the others. The axes were fine and firebug showed proper values for the JSON string.

After a bit of looking around, it seems that ExtJS does not convert from string numeric values to numeric values. Remember that mysql_fetch_assoc provides an associative array with string values. All that was needed was to manually cast to integer/float values that the LineChart needs.

The bit of modified code is below. Note the casts inside the while loop

        $graph_query="select 
				date(f_date) as gdate,
                sum(f1) as f1,
                sum(f2) as f2,
                sum(f3) as f3
                from stats
				where id=user
                and date(f_date)>date_sub(current_date, interval 7 day)
                group by date(f_date)";
        $graph_recordset=mysql_query($graph_query);
		while($graph_row=mysql_fetch_assoc($graph_recordset)){
			$graph_row['f1']=intval($graph_row['f1']); //or floatval as needed;
			$graph_row['f2']=intval($graph_row['f2]); //or floatval as needed;
			$graph_row['f3']=intval($graph_row['f3]); //or floatval as needed;
			$response['data']=$graph_row;
		}
		$response['success']=true;
		print json_encode($response);

Function to create traffic graph in PHP with gd

Just finished a couple of projects in which this code came in useful.

You just pass in an array of traffic date of the format
arrayname[0]['date']=’15/06′;
arrayname[0]['data']=200;
arrayname[1]['date']=’16/06′;
arrayname[1]['data']=100;

I am Indian so the data code is in format d/m, but m/d should not matter either if you create the array in proper order. Also if the data is greater than 1024 for any of the array values, the graph switches to GB display.

Here is a sample image generated by this code.

Sample graph

Sample graph

Below is the code, enjoy! Let me know if it helped you.

<?php
function generategraph($traffic){
$days=count($traffic);
$imagewidth=700; $imageheight=300;
$yaxistextarea=35; 	$lefttextarea=30; $xaxistextarea=30; $bottomtextarea=30;
$topmargin=20; 	$rightmargin=20; $graphtop=10;
$yaxissplits=10;
$datascale=1;
$dataunit='MBs';

$graphwidth=$imagewidth-$yaxistextarea-$lefttextarea-$rightmargin;
$graphheight=$imageheight-$xaxistextarea-$bottomtextarea-$topmargin-$graphtop;

//xaxis is one greater because one line the start and end of the graph will not have any bars on them.
$xaxissplits=$days+1;
$barwidth=$graphwidth/($xaxissplits+1)/2;

if ($barwidth>30) $barwidth=30;
$i=0;
$maxvalue=0;$minvalue=0;$totaldata=0;
foreach($traffic as $value){
if ($maxvalue<$value&#91;'data'&#93;) $maxvalue=$value&#91;'data'&#93;;
$totaldata+=$value&#91;'data'&#93;;
}

if ($maxvalue>=1024) { $datascale=1024; $dataunit='GBs';}

//The y axis is for the amount of traffic. so we find the minimum of the incoming and the outcoming traffic and
//it will be the lower and upper bounds.

//Since we will have a graph of only 20 lines, we split the difference into 20 and find the linerange
$linerange=($maxvalue-$minvalue)/$yaxissplits;
$graphscale=$graphheight/($maxvalue-$minvalue);

//we now have a neat map of i => in and out
//remember that each linerange is equal to
$im = ImageCreate ($imagewidth,$imageheight);
if (!$im) echo "Error in gd library";

//some of these variables are not used in the current code, but nice to let it remain in case future coding requires additions like borders around bars, lines etc.
imagecolorallocatealpha($im,255,255,255,100);
$imagebackground=ImageColorAllocate($im, 255,255,255);
$graphbackground=ImageColorAllocate($im, 255,255,255);
$textcolor=ImageColorAllocate($im, 100,100,100);
$datalinecolor=ImageColorAllocate($im, 100, 255,100);
$databarcolor=ImageColorAllocate($im, 233,131,18);
$databarbordercolor=ImageColorAllocate($im, 100, 100,100);
$axiscolor=ImageColorAllocate($im, 50,50,50);
$axislinecolormajor=ImageColorAllocate($im, 186,180,24);
$axislinecolorminor=ImageColorAllocate($im, 255,255,255);

//create some frequently used variables
//these are the min and max values on the graph.
$xmin=$yaxistextarea + $lefttextarea;
$ymin=$imageheight-$bottomtextarea-$xaxistextarea;
$xmax=$imagewidth-$rightmargin;
$ymax=$topmargin;

//create the graph area
imagefilledrectangle($im, $xmin, $ymin, $xmax, $ymax, $graphbackground);
imagestringup($im, 3, 0+5, $ymin-30 , "Data Transmitted in ".$dataunit, $textcolor);

for ($i=1; $i<=$yaxissplits; $i++){
//each major y axis line locations
$ymajor = $ymin - ($i * ($graphheight/$yaxissplits));
imagestring($im, 2,  $lefttextarea,$ymajor -5, round(($minvalue + $i * $linerange)/$datascale,2), $textcolor);
$yminor = $ymajor + ($graphheight/$yaxissplits)/2;
imageline($im, $xmin, $ymajor, $xmax, $ymajor, $axislinecolormajor);
}

for ($i=1; $i<$xaxissplits; $i++){
//each major x axis line locations
$xmajor = $xmin + ($i * ($graphwidth/$xaxissplits));
$xminor = $xmajor - ($graphwidth/$xaxissplits)/2;
imageline($im, $xmajor, $ymin, $xmajor, $ymax, $axislinecolormajor);
imagestringup($im, 1,  $xmajor-3, $ymin+$xaxistextarea, $traffic&#91;$i-1&#93;&#91;'date'&#93;, $textcolor);
}

//plot the actual graph
$plotxold=$xmin; $plotyold=$ymin;
for($key=1; $key<=$xaxissplits-1; $key++){
$value=$traffic&#91;$key-1&#93;;
$plotx=$xmin + ($key) *($graphwidth/$xaxissplits);
$ploty= $ymin - ($value&#91;'data'&#93; - $minvalue) * $graphscale;
if ($plotx>$xmax) $plotx=$xmax; //to avoid bleed due to rounding off of graphscale
if ($ploty<$ymax) $ploty=$ymax; //to avoid bleed due to rounding off of graphscale
$plotxold=$plotx; $plotyold=$ploty;
if ($value&#91;'data'&#93;>0){
imagefilledrectangle($im, $plotx-$barwidth/2, $ymin, $plotx+$barwidth/2, $ploty, $databarcolor);
}
}
$plotxold=$xmin; $plotyold=$ymin;
imagestring($im, 2,  $yaxistextarea+$lefttextarea,$imageheight-$bottomtextarea*3/4, "Total Data: " . $totaldata . " ".$dataunit, $textcolor);
imagestring($im, 1,  $lefttextarea,5, "Image generated at " . date('d/m/y  H:i'), $textcolor);
imagerectangle($im, $xmin, $ymin, $xmax, $ymax, $axiscolor);
imagejpeg($im, "graph.jpg");
return "<div><img src='graph.jpg' /></div>";
?>

Positioning text vertically and horizontally using gd

I was working on a PHP script yesterday for creating a couple of graphs which required lots of text in various places aligned to text on the same line. Some on the x axix and some on y. Finding no inbuilt function in gd on php, I created this simple function to align text vertically and horizontally as required based on the parameters passed to it.

[sourcecode language="php"]

function positiontextinimage($img, $font, $size, $color, $string, $x,$y, $horiz, $vert)
{
$bounds=imageftbbox  ( $size  , 0  , $font  , $string);
switch($horiz){
case “left”:
break;
case “right”:
$x=$x-$bounds[4];
break;
case “center”:
case “centre”:
$x=$x-($bounds[4] -$bounds[0])/2;
break;
}
switch($vert){
case “top”:
$y=$y-$bounds[5];
break;
case “bottom”:
break;
case “middle”:
$y=$y+($bounds[1]-$bounds[5])/2;

break;

}
imagefttext  ( $img  , $size  , 0  , $x, $y, $color, $font  , $string);
}

[/sourcecode]

PHP Proxy script to solve javascript and jquery cross domain issues

I just finished a project in which a jquery dialog used a jquery.ajax call using get to obtain details of a transaction from a url on another domain, effectively a cross domain ajax request. Since firefox 3.0 does not allow itself to be set to allow cross domain requests, I had to use some other way. I could have just used an iframe, but that is another story in itself.

Here is the bit of javascript code.

[sourcecode language="js"]

function refaxorder(){
//idfield=$(“#idfield”).val();
query=”urlid=1&inOID=” + idfield + “&inKey=” + key + “&inAction=reFax”;
$(“#actiondiv”).html(“

Loading…

“);
$(“#actiondivin”).dialog({autoOpen:false, modal:true, title:”ReFax”, buttons:{OK: function(){$(this).dialog(‘destroy’).remove(); }}});;
$(“#actiondivin”).dialog(“open”);

$.ajax({type:”GET”, url: “proxy.php”, data: query,  dataType:”html”, success:function(msg){
$(“#actiondivin”).html(msg);
}
});
}

[/sourcecode]

To get around this, I implemented a couple of lines as proxy.php to act as a proxy. Please note that I am only allowing it to be passed the urlid instead of a url to prevent it from becoming an open proxy. Also IP based filtering could be implemented to make it more safer. Have a look, maybe it will come in useful for you too.

[sourcecode language='php']

$value){
if ($key!=’url’) $query[]=$key.”=”.$value;
}
if (count($query)) $querystring=implode(“&”,$query);
$fullurl=$url[$urlid].”?”.$querystring;

$fp=fopen($fullurl,’rb’);
$page = ”;
if ($fp===false) die(“Unable to open”);
while (!feof($fp)) {
$page .= fread($fp, 8192);
}
fclose($fp);
print $page;
?>

[/sourcecode]

PHP and XML RPC – Searching for values

Many servers provide xmlrpc interfaces which allow other web applications to call and execute functions. It is actually quite simple once you get the hang of it. The servers also return the response and any variables as xmlrpc response messages which are xml responses basically

There are probably a lot of ways to get this done, but this actually turned out pretty well using a combination of the phpxmlrpc library and the Domdoc class in php.

Let us say that the server url is https://rpc.server.com/admin/admin at port 4567.
In order to make the call you first have to download the phpxmlrpc library from http://phpxmlrpc.sourceforge.net/ and extract it to a folder in your web directory root.

In the beginning you may want to turn the debugging feature of the rpc client on. To do uncomment the setDebug line in the code.

<?php

//error_reporting(E_ALL);

//ini_set("display_errors", 1);

require_once('xmlrpc-2.2.2/lib/xmlrpc.inc');

//You do not need to set the transport as https here.

$xmlrpc_client=new xmlrpc_client('admin/admin', 'rpc.server.com',4567);

//$xmlrpc_client->setDebug(1);

//An xmlrpc call without any parameters is below.

$xmlrpc_msg=new xmlrpcmsg('rpcFunctionName');

//the next one is an xmlrpc call with parameters.

//$xmlrpc_msg=new xmlrpcmsg('rpcFunctionName', array(new xmlrpcval(7, "int"),new xmlrpcval("", "string"),new xmlrpcval(2, "int")));

//Here is where you set the transport as https. Look at the manual for more options

$xmlrpc_resp=$xmlrpc_client->send($xmlrpc_msg, 200, 'https');

if ($xmlrpc_resp==False)

{

print "No response";

die ('Error');

}

if (!$xmlrpc_resp->faultCode())

{

//If you just want the xml, print out the return value from serialize()

//print $xmlrpc_resp->serialize() ;

$dom=new DomDocument();

$dom->loadXML($xmlrpc_resp->serialize());

//examine the xml to find out the path to the actual things you need.

$xpathString = "//methodResponse/params/param/value/struct/member[name='a_value']/value/string";

$xp=new DOMXPath($dom);

$domNodeList = $xp->query($xpathString);

foreach($domNodeList as $domNode){

//You may need to do more xpath queries as $xp->query($anotherXpathstr, $domNode). search will be done under $domNode

$server_name=$domNode->nodeValue ;

}

}

else

{

print "Error: " . $xmlrpc_resp->faultString();

}

?>

Also do look up the documentation for xmlrpc_client property return_type.

Solving the include_once error in Pear Mail

I have this hosting from hosting.india.to and helping on a friend’s community development initiative by setting up his website. It may soon move to careforindia.org.

During that, I set up a email form on my system, using PHP’s PEAR mail package to send mail. Though it worked fine on my own system, it didn’t on the hosting server. And no errors either.

First enabled the Error messages by putting this is the first line of the php file.

ini_set('display_errors','1'); ini_set('display_startup_errors','1'); error_reporting (E_ALL);

And then out popped the error.

Warning: Mail_smtp::include_once(Net/SMTP.php) [mail-smtp.include-once]: failed to open stream: No such file or directory in /home/username/php/Mail/smtp.php on line 206


Warning: Mail_smtp::include_once()[function.include]: Failed opening 'Net/SMTP.php' for inclusion (include_path='.:/usr/lib/php:/usr/local/lib/php') in /home/username/php/Mail/smtp.php on line 206


Fatal error: Class 'Net_SMTP' not found in /home/username/php/Mail/smtp.php on line 210

Apparently the basedir is set incorrectly and since I need to ask the hosting person to change the configuration, and to avoid the hassle, the simplest solution was to edit line 206 in /home/username/php/Mail/smtp.php to have

include_once '/home/username/php/Net/SMTP.php';

include_once 'Net/SMTP.php';

Yes, that is the full path.

Once I did that, I also got an error from Net/SMTP.php with it finding difficulty in locating Socket.php.

Same solution again and everything looks hunky dory. Clean up the error reporting and any debugging steps in between.

Oh, by the way, the site is at careforindia.jsemmanuel.com/donate.php and will soon be moved to www.careforindia.org