ObserverTutorial/WebService

Version 8 (modified by jean-gui, 3 years ago)

--

The calculator Web Service

An observer of unicorn is simply a web service outputting results in a Unicorn-compatible format. In this first part of the tutorial, we're going to create a calculator web service following the specifications described in the introduction.

The service will be implemented in PHP as this language is fairly easy to read.

The calculator service has three parameters:

  • uri: the URI of the file to process
  • x2: multiplies results by two if present (its value doesn't matter)
  • output: the format to use to output results. Only html is available for this first section

caculator.php

This is the main file. It contains the core functionnality of the calculator.

The following code is the entry point of the service. It checks that the uri parameter is present and that the corresponding resource exists before calling the function process_file.

<?php
if(array_key_exists('uri', $_GET)) {
    // File containing expressions to evaluate
    $file = fopen($_GET['uri'], 'r');
    
    if($file) {
        // Line count to track error lines
        $line = 0;
        $x2   = array_key_exists('x2', $_GET);

        // Array containing two elements, one for errors and one for valid results
        $results = process_file($file, $x2);
    }
    else {
        $results[ERROR]['N/A'] = array($_GET['uri'], 'Couldn\'t load URI');
    }
}
else {
    $results[ERROR]['N/A'] = array('N/A', 'No URI provided');
}
?>

The process_file function iterates over each line and calls process_expression which will be in charge of evaluating the expression.

<?php
/**
 * Process a whole file containing one arithmetic expression per line
 */
function process_file($file, $x2) {
    $results = array();
    // for each line
    while(!feof($file)) {
        $expr = trim(fgets($file));
        $line++;
        if($expr !== '') {
            // process the line and add the result to the corresponding array (errors or results)
            list($code, $result) = process_expression($expr, $x2);
            $results[$code][$line] = array($expr, $result);
        }
    }
    return $results;
}
?>

process_expression first performs a quick sanity check to ensure the expression contains only valid symbols (digits, operators and parentheses). It then calls eval to evaluate the expression. Note that using eval is pretty bad as it can be very harmful. The earlier sanity check should prevent any misuse though and eval is very handy in this case (otherwise we would have had to write the evaluator ourselves).

If eval throws an error, it actually immediately sends it to the default output buffer. That's why we use the ob_* functions. They allow to keep any output in a buffer instead of directly sending it to the browser

<?php
/**
 * Process an arithmetic expression
 */
function process_expression($expr, $x2) {
    // Quick sanity-check to only allow integers, operators and parentheses
    if(preg_match('/^[0-9*-+()\/]+$/', $expr)) {
        ob_start();
        // evaluate the expression, using eval is not nice and can be dangerous
        // but earlier sanity-check should prevent misuses
        $result = eval('return ' . $expr . ';');
        // get the error message if something went wrong
        $out = trim(ob_get_contents());
        ob_end_clean();
        if($out !== '') {
            // an error occurred
            return array(ERROR, $out);
        }
        else {
            if($x2) {
                $result *= 2;
            }
            return array(RESULT, $result);
        }
    }
    else {
        return array(ERROR, 'Forbidden characters');
    }
}
?>

The last part of the script is about rendering result in different formats. For now, we will only define a basic HTML output.

<?php
/*
 * Output results using separate templates
 */
$valid_formats[] = 'html';
$errors          = $results[ERROR];
$results         = $results[RESULT];

// Output format
$format = $_GET['output'];

if(!$format || !in_array($format, $valid_formats)) {
    $format = 'html';
}
include('calculator_' . $format . '.tpl');
?>

calculator_html.tpl

Template is a simple HTML document with embedded PHP in them. In this template, we iterate over errors and results and display them in definition lists.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Calculator Results for <?php echo $_GET['uri']; ?></title>
  </head>
  <body>
    <h1>Calculator Results for <?php echo $_GET['uri']; ?></h1>

    <h2>Errors</h2>
    <?php if(count($errors) > 0): ?>
      <dl>
      <?php foreach($errors as $line => $val): ?>
        <dt>Line: <?php echo $line; ?>, context: <?php echo $val[0]; ?></dt>
        <dd><?php echo $val[1]; ?>
      <?php endforeach ?>
      </dl>
    <?php endif ?>

    <h2>Results</h2>
    <?php if(count($results) > 0): ?>
      <dl>
      <?php foreach($results as $line => $val): ?>
        <dt>Line <?php echo $line; ?></dt>
        <dd><?php echo $val[0]; ?> = <?php echo $val[1]; ?></dd>
      <?php endforeach; ?>
      </dl>
    <?php endif ?>
  </body>
</html>

Attachments