<?php
/**
* phpPhrasebook: PHP Implementation of the Phrasebook pattern
*
* This class implements the phrasebook pattern, as documented by Yonat Sharon
* and Rani Pinchuk in their paper, The Phrasebook Pattern available at
* {@link http://jerry.cs.uiuc.edu/~plop/plop2k/proceedings/Pinchuk/Pinchuk.pdf}.
*
* @package phpPhrasebook
* @author Andrew Barilla <hide@address.com>
* @version 0.1
* @link http://phpphrasebook.sourceforge.net
* @copyright GNU-GPL
*
*/
/**
* Implements the Phrasebook pattern
*
* This class implements the phrasebook pattern, as documented by Yonat Sharon
* and Rani Pinchuk in their paper, The Phrasebook Pattern available at
* http://jerry.cs.uiuc.edu/~plop/plop2k/proceedings/Pinchuk/Pinchuk.pdf. It
* uses similar calls and XML document format to Rani Pinchuk's
* Class::Phrasebook module for Perl. The following documentation detailing
* the XML format is from the Class::Phrasebook module. (No reason to
* rewrite it)
*
* <snip>
*
* This class implements the Phrasebook pattern. It lets us create
* dictionaries of phrases. Each phrase can be accessed by a unique key. Each
* phrase may have placeholders. Group of phrases are kept in a dictionary.
* ... The phrases are kept in an XML document.
*
* The XML document type definition is as followed:
* <pre>
* <?xml version="1.0"?>
* <!DOCTYPE phrasebook [
* <!ELEMENT phrasebook (dictionary)*>
* <!ELEMENT dictionary (phrase)*>
* <!ATTLIST dictionary name CDATA #REQUIRED>
* <!ELEMENT phrase (#PCDATA)>
* <!ATTLIST phrase name CDATA #REQUIRED>
* ]>
* </pre>
* Example for XML file:
* <pre>
* <?xml version="1.0"?>
* <!DOCTYPE phrasebook [
* <!ELEMENT phrasebook (dictionary)*>
* <!ELEMENT dictionary (phrase)*>
* <!ATTLIST dictionary name CDATA #REQUIRED>
* <!ELEMENT phrase (#PCDATA)>
* <!ATTLIST phrase name CDATA #REQUIRED>
* ]>
* <phrasebook>
* <dictionary name="EN">
*
* <phrase name="HELLO_WORLD">
* Hello World!!!
* </phrase>
*
* <phrase name="THE_HOUR">
* The time now is $hour.
* </phrase>
*
* <phrase name="ADDITION">
* add $a and $b and you get $c
* </phrase>
*
*
* <!-- my name is the same in English Dutch and French. -->
* <phrase name="THE_AUTHOR">
* Rani Pinchuk
* </phrase>
* </dictionary>
*
* <dictionary name="FR">
* <phrase name="HELLO_WORLD">
* Bonjour le Monde!!!
* </phrase>
*
* <phrase name="THE_HOUR">
* Il est maintenant $hour.
* </phrase>
*
* <phrase name="ADDITION">
* $a + $b = $c
* </phrase>
*
* </dictionary>
*
* <dictionary name="NL">
* <phrase name="HELLO_WORLD">
* Hallo Werld!!!
* </phrase>
*
* <phrase name="THE_HOUR">
* Het is nu $hour.
* </phrase>
*
* <phrase name="ADDITION">
* $a + $b = $c
* </phrase>
*
* </dictionary>
*
* </phrasebook>
* </pre>
*
* Each phrase should have a unique name. Within the phrase text we can
* place placeholders. When get method is called, those placeholders will be
* replaced by their value.
*
* </snip>
*
* <b>Example:</b>
*
* <pre>
*
* require ("Phrasebook.php");
*
* $hPhrase = new Phrasebook("mydict.xml");
* $hPhrase->load("EN");
* print $hPhrase->getPhrase("ADDITION", array("a"=>"1",
* "b"=>"2",
* "c"=>"3"));
*
* </pre>
*
* @package phpPhrasebook
* @author Andrew Barilla <hide@address.com>
*
*/
class Phrasebook {
/**#@+
* @access private
*/
var $stFileName;
var $stDictionary;
var $astPhrases;
var $stCurrentTag;
var $stPhraseName;
var $stPhraseValue;
var $boReadOn;
var $boRemoveNewLines;
/**#@-*/
/**
* Constructor for Phrasebook class.
*
* @param string a_stFileName
* The filename of the xml phrasebook document to read.
* @see setFileName
*/
function Phrasebook ($a_stFileName) {
$this->setFileName($a_stFileName);
$this->stPhrases = array();
$this->boRemoveNewLines = false;
}
/**
* Registers the name of the XML phrasebook file to use next time the
* load function is called. Currently, this looks in the working directory
* for the XML file.
*
* @param string a_stFileName
* The filename of the xml phrasebook document to read.
*/
function setFileName ($a_stFileName) {
$this->stFileName = $a_stFileName;
}
/**
* Loads the dictionary from the xml file into memory. Calling this
* more than once will not reset the previous dictionary but will instead
* override and add to it.
*
* @param string a_stDictionary
* The filename of the dictionary to read from the phrasebook
*/
function load ($a_stDictionary = "") {
if ($a_stDictionary != "")
$this->setDictionaryName ($a_stDictionary);
$l_hXmlParser = xml_parser_create();
xml_set_element_handler($l_hXmlParser,
array(&$this, "_startElement"),
array(&$this, "_endElement"));
xml_set_character_data_handler($l_hXmlParser,
array(&$this, "_characterData"));
if (!($l_hFile = fopen($this->stFileName, "r"))) {
die("Cannot locate XML data file: ".$this->stFileName);
}
$this->boReadOn = false;
$this->stPhraseName = "";
$this->stPhraseValue = "";
// read and parse data
while ($l_stData = fread($l_hFile, 4096)) {
if (!xml_parse($l_hXmlParser, $l_stData, feof($l_hFile))) {
$stError = xml_error_string(xml_get_error_code($l_hXmlParser));
die(sprintf("XML error: %s at line %d",
$stError,
xml_get_current_line_number($l_hXmlParser)));
}
}
xml_parser_free($l_hXmlParser);
}
/**
* Returns the phrase from the dictionary and will substitute
* variables when appropriate.
*
* @param string a_stName
* The key of the phrase to return
* @param array a_astValues
* Array of strings to replace variables in the phrase with
* @return string
* The phrase with variables replaced if appropriate
*
*/
function getPhrase ($a_stName, $a_astValues = array()) {
if (!array_key_exists($a_stName, $this->astPhrases))
return "";
$stPhrase = $this->astPhrases[$a_stName];
$stPhrase = preg_replace('/(\$)([a-zA-Z0-9_]+)/ie',
"\$a_astValues['$2']",
$stPhrase);
$stPhrase = preg_replace('/(\$\()([a-zA-Z0-9_]+)\)/ie',
"\$a_astValues['$2']",
$stPhrase);
if ($this->boRemoveNewLines)
$stPhrase = str_replace('\n', '', $stPhrase);
return $stPhrase;
}
/**
* Returns the name of the dictionary currently in use
*
* @return string
* The name of the current dictionary
*
*/
function getDictionaryName () {
return $this->stDictionary;
}
/**
* Registers the name of the dictionary to use next time
* the load function is called.
*
* @param string a_stDictionary
* The name of the dictionary to use
*
*/
function setDictionaryName ($a_stDictionary) {
$this->stDictionary = $a_stDictionary;
}
/**
* Returns the value of the RemoveNewLines setting
*
* @return boolean
* The current value of the RemoveNewLines setting
*
*/
function getRemoveNewLines () {
return $this->boRemoveNewLines;
}
/**
* Sets the value of the RemoveNewLines setting. When true,
* new lines will be stripped when calling the getPhrase method.
* When false, will leave phrases intact.
*
* @param boolean a_boRemoveNewLines
* The value of the RemoveNewLines setting
*
*/
function setRemoveNewLines ($a_boRemoveNewLines) {
$this->boRemoveNewLines = $a_boRemoveNewLines;
}
/**#@+
* @access private
*/
function _startElement ($a_hParser, $a_stName, $a_astAttrs) {
$this->stCurrentTag = $a_stName;
$this->stPhraseValue = "";
switch ($a_stName) {
case "DICTIONARY":
if (is_array($a_astAttrs)) {
$stDictionary = $a_astAttrs["NAME"] or
die("The dictionary element must " .
"have the name attribute.");
if ($stDictionary == $this->stDictionary) {
$this->boReadOn = true;
}
else {
$this->boReadOn = false;
}
}
else {
die("The dictionary element must have the name attribute.");
}
break;
case "PHRASE":
if ($this->boReadOn) {
if (is_array($a_astAttrs)) {
$stPhraseName = $a_astAttrs["NAME"] or
die("The phrase element must " .
"have the name attribute.");
$this->stPhraseName = $stPhraseName;
$this->stPhraseValue = "";
}
}
break;
}
}
function _endElement ($a_hParser, $a_stName) {
if ($this->boReadOn && strlen($a_stName)) {
$this->astPhrases[$this->stPhraseName] = $this->stPhraseValue;
}
$this->stCurrentTag = "";
}
function _characterData ($a_hParser, $a_stData) {
$this->stPhraseValue .= $a_stData;
}
/**#@-*/
}
?>