Skip to content

Commit

Permalink
Initial commit - minimally functional FOML implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
guyc committed May 22, 2012
1 parent 06c4f46 commit 0972bc5
Show file tree
Hide file tree
Showing 21 changed files with 683 additions and 3 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fop-1.0/
*~
102 changes: 102 additions & 0 deletions Foml.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

require_once 'FomlConfig.php';

class FOML
{
static $fopExec = "fop-1.0/fop"; // fopExec is relative to this directory
static $pdfMimeType = "application/pdf";
static $tempDir = null; // defaults to system temp directory
static $keepTempFiles = true; // set to true for debugging

static function GeneratePhp($Template)
{
$fomlParser = new FomlParser();
$php = $fomlParser->ParseFile($Template);
return $php;
}

// returns XSL-FO string
// REVISIT - use ob_start with a callback function to capture output directly to a file,
// and return a temporary filename instead of a string.
static function GenerateXslFo($Template, $Args=null)
{
// import variables
if ($Args) {
foreach ($Args as $key=>$value) {
$$key = $value;
}
}
$_php = FOML::GeneratePhp($Template);

ob_start();
eval("?".">".$_php); // prefixed with ? > to exit implicit php mode
$xslFo = ob_get_contents();
ob_end_clean();
return $xslFo;
}

static function TempName($Prefix)
{
$tempDir = FOML::$tempDir;
if ($tempDir == null) $tempDir = sys_get_temp_dir();
return tempnam($tempDir, $Prefix);
}

// returns the name of the temporary pdf output file.
// The file must be unlinked by the caller.
static function XslFoToPdf($XslFo)
{
$pdfFileName = FOML::TempName("pdf-");
$xslFoFileName = FOML::TempName("xslfo-");

$xslFile = fopen($xslFoFileName, "w");
fwrite($xslFile, $XslFo);
fclose($xslFile);

$escapedPdfFileName = escapeshellarg($pdfFileName);
$escapedXslFoFileName = escapeshellarg($xslFoFileName);
$fop = dirname(__FILE__).'/'.FOML::$fopExec;
$cmd = "{$fop} {$escapedXslFoFileName} {$escapedPdfFileName}";
shell_exec($cmd);

if (!FOML::$keepTempFiles) unlink($xslFoFileName);

return $pdfFileName;
}

static function Render($Template, $Args=null, $Headers=null)
{
$xslFo = FOML::GenerateXslFo($Template, $Args);
$pdfFileName = FOML::XslFoToPdf($xslFo);
$size = filesize($pdfFileName);
$mimeType = FOML::$pdfMimeType;

if ($Headers) {
foreach ($Headers as $header) {
header($header);
}
}
header("Content-Length: {$size}");
header("Content-Type: {$pdfMimeType}");

$fileHandle = fopen($pdfFileName, "rb");
fpassthru($fileHandle);
fclose($fileHandle);

if (!FOML::$keepTempFiles) unlink($pdfFileName);
}
static function RenderInline($Template, $Args=null)
{
$headers = array("Content-Disposition"=>"inline");
FOML::Render($Template, $Args, $headers);
}

static function RenderAttachment($Template, $Filename, $Args=null)
{
$headers = array("Content-Disposition: attachment; filename=\"{$FileName}\"");
FOML::Render($Template, $Args, $headers);
}
}

?>
22 changes: 22 additions & 0 deletions FomlCommentNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php
class FomlCommentNode extends FomlNode
{
const MATCH_RE = "/^\/(.*)/";

function __construct($Matches)
{
$this->text = $Matches[1];
}

function RenderPrefix()
{
// todo - should we escape inner "-->" to avoid early closing?
print "<!-- {$this->text} ";
}

function RenderSuffix()
{
print "-->\n";
}
}
?>
23 changes: 23 additions & 0 deletions FomlConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

/*
if you are using an autoloader, you can remove all of these
requires and let the autoloader lazy-load the classes
*/

require_once 'FomlParser.php';
require_once 'FomlNode.php';

require_once 'FomlCommentNode.php';
require_once 'FomlDoctypeNode.php';
require_once 'FomlExecNode.php';
require_once 'FomlFilterNode.php';
require_once 'FomlInsertNode.php';
require_once 'FomlTagNode.php';
require_once 'FomlTextNode.php';
require_once 'FomlExecNode.php';

require_once 'FomlDoc.php';
require_once 'FomlParseTree.php';

?>
29 changes: 29 additions & 0 deletions FomlDoc.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php
class FomlDoc extends FomlNode
{

function RenderToString()
{
ob_start();
$this->Render();
$output = ob_get_contents();
ob_end_clean();
return $output;
}

function EvalToString($Args)
{
foreach ($Args as $key=>$value) {
$$key = $value;
}

$php = $this->RenderToString();
ob_start();
eval("?".">".$php);
$output = ob_get_contents();
ob_end_clean();
return $output;
}

}
?>
23 changes: 23 additions & 0 deletions FomlDoctypeNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php
class FomlDoctypeNode extends FomlNode
{
const MATCH_RE = "/^\!\!\!\s*/";

function __construct($Matches)
{
// Nothing to save.
}

function RenderPrefix()
{
print '<?php echo \'<?xml version="1.0" encoding="utf-8"?>\'?>';
}

function RenderSuffix()
{
}



}
?>
49 changes: 49 additions & 0 deletions FomlExecNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php
class FomlExecNode extends FomlNode
{
const MATCH_RE = "/^-\s*(.*)/";
/*
* Examples
* - $x = 0
* - foreach ($i=0;$i<10;$i++)
*/

/*
* else and elseif are interesting. They don't need special handling - the tree
* will naturally render like this:
* < ? php if (condition) { ? >
* stuff
* < ? php } ? >
* < ? php else { ? >
* more stuff
* < ? php } ? >
* because
*/

public $code;
public $hasBlock; // if true enclose block in {} otherwise end command with ";"

function __construct($Matches)
{
$this->code = $Matches[1];
$this->hasBlock = preg_match("/^(foreach|for|while|if|else|elseif)/", $this->code);
}

function RenderPrefix()
{
print "<?php ";
print $this->code;
if ($this->hasBlock) {
print "{ ?>";
} else {
print "; ?>";
}
}

function RenderSuffix()
{
if ($this->hasBlock) {
print "<?php } ?>";
}
}
}
12 changes: 12 additions & 0 deletions FomlFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
class FomlFilter
{
static function CreateAndRender($Args)
{
$filterClass = get_called_class();
$filter = new $filterClass($Args);
$filter->Render();
}
}

?>
31 changes: 31 additions & 0 deletions FomlFilterNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

// TODO - filter doesn't yet pass child nodes to filter
// For now I'm just using it for :include('file.foml')

class FomlFilterNode extends FomlNode
{
// this re is probably not rich enough to support args using variable expansion
const MATCH_RE = "/^:([a-zA-Z0-9]+)(\((.*)\))?/";

public $filterClass;

function __construct($Matches)
{
$filter = $Matches[1];
$args = $Matches[3];
if (isset(FomlParser::$FILTER_CLASSES[$filter]))
{
$this->filterClass = FomlParser::$FILTER_CLASSES[$filter];
$this->args = $args;
} else {
throw new FomlException("Unknown filter '{$filter}'");
}
}

function RenderPrefix()
{
print "<?php {$this->filterClass}::CreateAndRender({$this->args}) ?>\n";
}

}
20 changes: 20 additions & 0 deletions FomlIncludeFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php
class FomlIncludeFilter extends FomlFilter
{
public $fileName;

function __construct($Arg)
{
$this->fileName = $Arg;
}

function Render()
{
// REVISIT : can't just print the PHP here because we are
// called from within the eval context. However doing it this
// way hides all of the variables, which might be a problem.
eval('?'.'>'. FomlParser::ParseFile($this->fileName));
}
}

?>
18 changes: 18 additions & 0 deletions FomlInsertNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
class FomlInsertNode extends FomlNode
{
const MATCH_RE = "/^=\s*(.*)/";

function __construct($Matches)
{
$this->code = $Matches[1];
}

function RenderPrefix()
{
print "<?php print ";
print $this->code;
print "; ?>";
print " \n";
}
}
23 changes: 23 additions & 0 deletions FomlNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php
class FomlNode
{
public $children = array();

function RenderPrefix()
{
}

function RenderSuffix()
{
}

function Render($Indent=0)
{
$this->RenderPrefix();
foreach ($this->children as $child) {
$child->Render();
}
$this->RenderSuffix();
}
}
?>
Loading

0 comments on commit 0972bc5

Please sign in to comment.