I posted a few questions while trying to get Open Flash Charts to work to display a compound interest chart in my Drupal 5.2 site. In the discussions that followed, I learned that at least a few others are interested in using the charts and have been having trouble doing so. As a result, I thought I'd share my difficulties and solutions.

Problems encountered:

  • Problems with including open flash chart includes when hosting includes and PHP data scripts in a "charts" folder one level under root.
  • Difficulty setting input parameters from a Drupal-hosted form into the $_SESSION such that they can be retrieved from a plain vanilla PHP script that feeds the SWF with charting data.

Solution for Include path problem:
I copied open-flash-chart.php and open_flash_chart_object.php into the /includes folder. It appears that my problem with the includes was caused by an include path problem. Rather than researching how to add my "charts" to the path, the quick and easy move into "includes" was sufficient for my needs.

Solution to session sharing problem:
In order for the PHP script that feeds the SWF with charting data to be able to read the Drupal session, I needed to call the bootstrap as found in index.php:

require_once './includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

Important: It would appear that the drupal_bootstrap() can only be invoked from PHP scripts in the root. I have tried placing my scripts in the "charts" folder and updating the require_once pathname accordingly, but the bootstrap failed.

Drupal page content in "PHP" input format: (Displays chart, and accepts input variables)

<?php

include_once './includes/open_flash_chart_object.php';

// We need a unique suffix to prevent caching on the SWF's get:
$uniq = $_SESSION['ex100_uniq'];
$uniq = $uniq + 1;
$_SESSION['ex100_uniq'] = $uniq;

if ($_POST['posted'] == '1') {
  $ex100_num_years = $_POST['ex100_num_years'];
  $ex100_rate1 = $_POST['ex100_rate1'];
  $ex100_rate2 = $_POST['ex100_rate2'];
  $ex100_rate3 = $_POST['ex100_rate3'];
  $ex100_principle = $_POST['ex100_principle'];
  $ex100_start_year = $_POST['ex100_start_year'];

//} else if (isset($_SESSION['ex100_num_years'])) {
//  $ex100_num_years = $_SESSION['ex100_num_years'];
//  $ex100_rate1 = $_SESSION['ex100_rate1'];
//  $ex100_rate2 = $_SESSION['ex100_rate2'];
//  $ex100_rate3 = $_SESSION['ex100_rate3'];
//  $ex100_principle$_SESSION['ex100_principle'];
//  $ex100_start_year$_SESSION['ex100_start_year'];

} else {
  $ex100_num_years = 20;
  $ex100_rate1 = 3;
  $ex100_rate2 = 7;
  $ex100_rate3 = 11;
  $ex100_principle = 100;
  $ex100_start_year = 2007;
}

$_SESSION['ex100_num_years'] = $ex100_num_years;
$_SESSION['ex100_rate1'] = $ex100_rate1;
$_SESSION['ex100_rate2'] = $ex100_rate2;
$_SESSION['ex100_rate3'] = $ex100_rate3;
$_SESSION['ex100_principle'] = $ex100_principle;
$_SESSION['ex100_start_year'] = $ex100_start_year;

// ?x=nnn suffix keeps the data from being cached, nothing more:
open_flash_chart_object( 650, 500, 'http://www.math2millions.com/chart-ex100.php?x='.$uniq, false);

?>

<form name='chart' method='post' action='/compounding-rate-comparison'>
<input type="hidden" name="posted" value="1">

<label for='ex100_start_year'>Start Year:</label><input type='text' name='ex100_start_year' maxlength='4' size='4' value='<?php echo $ex100_start_year; ?>'>

<label for='ex100_num_years'>Number of Years:</label><input type='text' name='ex100_num_years' maxlength='3' size='3' value='<?php echo $ex100_num_years; ?>'><br>

<label for='ex100_rate1'>Rate 1:</label><input type='text' name='ex100_rate1' maxlength='5' size='3' value='<?php echo $ex100_rate1; ?>'>%

<label for='ex100_rate2'>Rate 2:</label><input type='text' name='ex100_rate2' maxlength='5' size='3' value='<?php echo $ex100_rate2; ?>'>%

<label for='ex100_rate3'>Rate 3:</label><input type='text' name='ex100_rate3' maxlength='5' size='3' value='<?php echo $ex100_rate3; ?>'>%<br>

<label for='ex100_principle'>Principle:</label><input type='text' name='ex100_principle' maxlength='8' size='8' value='<?php echo $ex100_principle; ?>'><br>

<input type="submit" name="subm" value="Refresh">
</form>

Contents of chart-ex100.php which feeds SWF with charting data: (MUST be in the root due to the drupal_bootstrap)

<?php

require_once './includes/open-flash-chart.php';
require_once './includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

function factor_to_percentage($factor) {
	$percentage = (($factor - 1) * 100);
	$percentage = $percentage.'%25';
	return $percentage;
}

function round_y_max($y_max) {

	if ($y_max > 1000000) {
		$round_max = (floor($y_max / 100000) + 1) * 100000;
	} else if ($y_max > 100000) {
		$round_max = (floor($y_max / 10000) + 1) * 10000;
	} else if ($y_max > 10000) {
		$round_max = (floor($y_max / 1000) + 1) * 1000;
	} else if ($y_max > 1000) {
		$round_max = (floor($y_max / 100) + 1) * 100;
	} else if ($y_max > 100) {
		$round_max = (floor($y_max / 10) + 1) * 10;
	} else if ($y_max > 10) {
		$round_max = (floor($y_max / 1) + 1) * 1;
	} else {
		$round_max = $y_max;
	}
	return $round_max;
}

function find_global_max($all_data) {
	
	$max = -2000000000;

	foreach($all_data as $chunk) {
		foreach($chunk as $single_val) {
			if ($single_val > $max) {
				$max = $single_val;
			}
		}
	}
	return $max;
}

$data_1 = array();
$data_2 = array();
$data_3 = array();
$x_label = array();

$start_year = $_SESSION['ex100_start_year'];
$num_years = $_SESSION['ex100_num_years'];
$principle = $_SESSION['ex100_principle'];
$rate1 = $_SESSION['ex100_rate1'];
$rate2 = $_SESSION['ex100_rate2'];
$rate3 = $_SESSION['ex100_rate3'];

if ($start_year < 10) {
	$start_year = $start_year + 2000;
} else if ($start_year < 100) {
	$start_year = $start_year + 1900;
}

if (!is_numeric($num_years)) {
	$num_years = 30;
}

if (is_numeric($rate1)) {
	if ($rate1 < 1) {
		$rate1 = (1 + $rate1);
	} else {
		$rate1 = (1 + ($rate1/100.0));
	}
} else {
	$rate1 = 1;
}

if (is_numeric($rate2)) {
	if ($rate2 < 1) {
		$rate2 = (1 + $rate2);
	} else {
		$rate2 = (1 + ($rate2/100.0));
	}
} else {
	$rate2 = 1;
}

if (is_numeric($rate3)) {
	if ($rate3 < 1) {
		$rate3 = (1 + $rate3);
	} else {
		$rate3 = (1 + ($rate3/100.0));
	}
} else {
	$rate3 = 1;
}

$data_1[0] = $principle;
$data_2[0] = $principle;
$data_3[0] = $principle;

$x_label[0] = $start_year;

for( $i=1; $i<$num_years; $i++ )
{
	$data_1[$i] = $data_1[$i-1] * $rate1;
	$data_2[$i] = $data_2[$i-1] * $rate2;
	$data_3[$i] = $data_3[$i-1] * $rate3;
	$x_label[$i] = $x_label[$i-1] + 1;
}

// Round to pennies:
for( $i=0; $i<$num_years; $i++ )
{
	$data_1[$i] = number_format($data_1[$i],2,'.','');
	$data_2[$i] = number_format($data_2[$i],2,'.','');
	$data_3[$i] = number_format($data_3[$i],2,'.','');
}

$y_max = find_global_max(array($data_1, $data_2, $data_3));
$y_max = round_y_max($y_max);

$g = new graph();

$g->title( '$'.$principle.' Compounded Over '.$num_years.' Years', '{font-size: 22px; color: #000000}' );

// we add 3 sets of data:
$g->set_data( $data_1 );
$g->set_data( $data_2 );
$g->set_data( $data_3 );

$g->line_dot( 3, 5, '#8BB1A1', 'Rate: '.factor_to_percentage($rate1), 10);
$g->line_hollow( 2, 4, '#BAA8BD', 'Rate: '.factor_to_percentage($rate2), 10 );
$g->line_dot( 3, 5, '#0000ff', 'Rate '.factor_to_percentage($rate3), 10);

$g->set_x_labels( $x_label );
$g->set_y_max( $y_max );

$g->set_inner_background( '#E3F0FD', '#CBD7E6', 90 );

$g->x_axis_colour( '#8499A4', '#E4F5FC' );
$g->y_axis_colour( '#8499A4', '#E4F5FC' );
$g->bg_colour = '#E4F5FC';

$g->y_label_steps( 10 );
$g->set_y_legend( 'Dollars', 12, '0x736AFF' );
$g->set_x_legend( 'Year', 12, '0x736AFF' );
$g->set_x_label_style( 11, '#000000', 2 );

echo $g->render();

?>

Comments

Heine’s picture

Please, do not output user data verbatim, or you'll create a lot of cross site scripting holes. Use the appropriate filtering/escaping before output. In this case check_plain().

Another good link: Handle text in a secure fashion in our Writing secure code guide.
--
The Manual | Troubleshooting FAQ | Tips for posting | How to report a security issue.

jbernat’s picture

Thanks for the advice on XSS.

Jim Bernatowicz

codenamerhubarb’s picture

Thanks so much for sharing your code. I was having trouble making the value of set_y_max set itself to be just above the maximum value of the data in my array and your code helped me a lot.

-------------------------------
My Drupal site: Download a Book.

-------------------------------
My Drupal site: Download a Book.