<?php
function replace_tag_data ($control_file, $subordinate_file, $description_file="default.desc", $use_file=false)
/* Replace html tag section contents based on rules specified in a description file.
string replace_tag_data ( string control_file, string subordinate_file, string description_file, boolean use_file)
control_file and subordinate_file contain html source (their file extensions do not need to be .htm, .html, etc.).
control_file is the name of a file whose contents determine the contents of one or more tag-identified sections in subordinate_file.
subordinate_file is the name of a file whose contents are determined by one or more tag-identified sections in control_file.
description_file is the name of a file containing text replacement rules for how the contents of control_file affect the contents of subordinate_file. If not specified, the function attempts to use "default.desc".
use_file is optional. If set to TRUE by the caller, the replacement value field described below specifies the name of a replacement file whose contents are used to replace contents in the subordinate file, providing for easy div, span, table, and other potentially large text replacements. The default value of FALSE indicates that the replacement value field in the rule is the actual replacement string to be used.
The function returns a string containing the subordinate_file contents, with all replacements specified by the rules in description_file.
The function returns FALSE if any of the following occurs: control, subordinate, description, or replacement file does not exist; error reading any of files.
If a tag, attribute, or attribute value is not found when indicated by a rule that it should exist in either the control or subordinate file, the functions ignores the rule and returns unmodifed subordinate file contents (i.e., it does not return FALSE).
Each line in the description_file contains the following elements:
(
control-tag (e.g., body)
control-attribute (e.g., id)
control-attribute-value (e.g., "home-page")
subordinate-tag (e.g., title)
subordinate-attribute (e.g., . period specifies that the rule applies for any attribute (or none) for the tag)
subordinate-attribute-value (e.g., . period specifies that the rule applies for any attribute (or none) for the tag)
replacement-value (e.g., "Home-Grown Websites: Your Source for Unique Web Design", if use_file is FALSE)
)
For example, a description file site-rules.desc containing the following descriptor:
(body id "home-page" title . . "Home-Grown Websites: Your Source for Unique Web Design")
with this function call:
replace_tag_data ("home-page-body.txt", "std-html-header.txt", "site-rules.desc");
would search home-page-body.txt for a <body> tag with an id attribute of "home-page". If that combination were present in home-page-body.txt, any text between the <title> and </title> tags in std-html-header.txt would be replaced with "Home-Grown Websites: Your Source for Unique Web Design" and the new version of std-html-header.txt is returned in a string to the caller. The contents of std-html-header.txt are undisturbed.
A description such as:
(body id "home-page" div "class" "nav-head" "navigation-bar.html")
with this function call:
replace_tag_data ("home-page-body.txt", "home-page-body.txt", "site-rules.desc", true);
would search home-page-body.txt for a <body> tag with an id attribute of "home-page". If that combination were present in home-page-body.txt, any text between <div class="nav-head"> and </div> tags in home-page-body.txt would be replaced with the entire contents of the file "navigation-bar.html". The new version of home-page-body.txt is returned in a string to the caller. All files on disk remain undisturbed. This example demonstrates that a file's contents can drive replacements in itself; i.e., the same file can be both the control and subordinate file.
Also note that multiple rules can be applied to the same subordinate file.
As stated above, if any of control-tag, control-attribute, or control-attribute-value does not match a rule, that rule has no effect on the subordinate file. Rule descriptions are enclosed in parentheses (); whitespace separates descriptor elements and is ignored, except when found in double-quoted text, in which it is preserved). Each rule must reside on its own line in the file.
Attribute and replacement values must be enclosed in double quotes, unless the null value is specified using the single period character. Parentheses can be freely used within attribute and replacement text; do not escape parentheses.
Known Problems:
Only replacement values can contain whitespace in the double-quoted string. Attribute values must not contain whitespace. There can be no whitespace between control tags and control attribute values (i.e., no space around the equals sign). No whitespace can precede the equals sign for subordinate attribute values.
Can replace text only for tags that have an end tag (e.g., <tr></tr>). Cannot handle more than one subordinate descriptor for each controlling descriptor; multiple replacements in the subordinate file must be specified using multiple rules).
Future Refinements: Fix the limitations noted above, especially to allow multiple replacements driven by a single control descriptor. I.e., allow rules like:
(body id "home-page" title . . "Home-Grown Websites: Your Source for Unique Web Design"; style type "text/Javascript" "<!-- tags.P.fontWeight=\"bold\"; -->"; style type "text/css" "<!-- p {font-weight: bolder}; -->")
(body id "contact-info" title . . "Home-Grown Websites: Contact Information"; h3 class "page-head" "How to Contact Us")
Other enhancements: 1) allow descriptions to be commented out; 2) allow double quotes within replacement text to be escaped with the backslash character, like this: "Martha says that's a \"Good Thing\""
*/
{ /* begin replace_tag_data */
$ret_val = false; // initialize to function failure
$error_string ='<!-- '; // prepend error text to resulting html source
$str_pos = ''; // initialize string position variable
/* get description_file contents into memory buffer */
if ($handle = fopen($description_file,"rb")) {
while (!feof ($handle)) {
$desc_buffer[] = fgets($handle, 2048); // read each rule into array element
}
fclose($handle); // close description file; got everything we need
unset ($desc_buffer[count($desc_buffer)-1]); // wipe final empty element
$ret_val = true;
}
$error_string .= !$ret_val ? 'Failed open of description file;' : '';
/* get control_file contents into memory buffer */
if ($ret_val) {
if ($handle = fopen($control_file,"rb")) {
if ( ($ctrl_buffer = file_get_contents($control_file))===false ) {
$ret_val = false; // file_get_contents failed on control_file
$error_string .= 'Failed reading control file contents (open was successful);';
}
fclose($handle);
} else {
/* fopen failed on control file */
$ret_val = false;
$error_string .= 'Failed open of control file;';
}
}
/* get subordinate_file contents into memory buffer */
if ($ret_val) {
if ($handle = fopen($subordinate_file,"rb")) {
if ( ($sub_buffer = file_get_contents($subordinate_file))===false ) {
$ret_val = false; // file_get_contents failed on subordinate_file
$error_string .= 'Failed reading subordinate file contents (open was successful);';
}
fclose($handle);
} else {
/* fopen failed on subordinate file */
$ret_val = false;
$error_string .= 'Failed open of subordinate file;';
}
}
if ($ret_val) {
$ret_val=$sub_buffer; // even if no replacements occur, return html text to the caller
/* traverse array of raw lines, extract replacement rule(s), then apply rules to subordinate file */
reset($desc_buffer);
while (list($key, $value)= each ($desc_buffer)) {
/* extract a replacement rule */
$num_elements = sscanf($value, " ( %s %s %s %s %s %s %s)",$ctrl_tag, $ctrl_attr, $ctrl_attr_val, $sub_tag, $sub_attr, $sub_attr_val, $replace_str);
//$error_string .= '
';
/* remove from line items already consumed (need this for subsequent sscanfs)*/
$value = substr($value, strpos($value, $replace_str) + strlen($replace_str));
/* remove leading " char from replacement string */
$replace_str = substr_replace($replace_str, '', 0, 1);
/* if the final char of the replacement string is not ), there is more text to collect */
while ($replace_str{strlen($replace_str)-1} != ')') {
/* get the next piece of the open string */
$num_elements = sscanf($value, "%s", $next_piece);
$replace_str .= ' '.$next_piece;
/* remove from line the item just consumed */
$value = substr($value, strlen($next_piece)+1);
} /* end while; got all of the replacement text */
$replace_str = substr_replace($replace_str, '', -2); // remove trailing ") characters
/* apply rules to subordinate file */
/* find first occurrence of the appropriate tag in control file */
if ( ($start_pos = strpos($ctrl_buffer,"<$ctrl_tag") ) === false) {
/* this tag is not present in the control file */
$error_string .= 'Tag not present in control file;';
} else {
/* we know control-tag is present, now find control-attribute and its value */
$str_pos += strlen("<$ctrl_tag");
if (strpos($ctrl_buffer,"$ctrl_attr=$ctrl_attr_val", $start_pos) === false) {
/* either the attribute or the necessary value is not present in the control file */
$error_string .= 'Attribute or value not present in control file;';
} else {
/* attribute and value are in controller_file, so try to replace text in subordinate_file */
/* find first occurrence of the appropriate tag in subordinate file */
if ( ($start_pos = strpos($sub_buffer,"<$sub_tag") ) === false) {
/* tag not present in the subordinate file */
$error_string .= $sub_tag.'Tag not present in subordinate file;';
} else {
/* we know subordinate-tag is present, now find subordinate-attribute */
$start_pos += strlen("<$sub_tag");
if ( ($sub_attr == '.' ? true : $start_pos = strpos($sub_buffer,"$sub_attr=", $start_pos)) === false) {
/* attribute not present in the subordinate file */
$error_string .= 'Attribute not present in subordinate file;';
} else {
/* attribute is present (or none specified), now find subordinate-attribute-value */
// assertion: $start_pos !== false or $sub_attr == '.'
$start_pos += $sub_attr == '.' ? 0 : strlen("$sub_attr=");
if ( ($sub_attr_val == '.' ? true : $start_pos=strpos($sub_buffer,$sub_attr_val, $start_pos) ) === false) {
/* subordinate-attribute-value not present */
$error_string .= 'Attribute value not present in subordinate file;';
} else {
/* subordinate tag, attribute, and value present, replace the text */
// assertion: $start_pos !== false or $sub_attr_val == '.'
$start_pos += $sub_attr_val == '.' ? 0 : strlen($sub_attr_val);
$start_pos = strpos($sub_buffer,'>',$start_pos)+1; // find replacement start point
//$error_string .= '|'.substr($sub_buffer, $start_pos, 10).'|'."\r\n";
$end_pos = strpos($sub_buffer,"</$sub_tag>",$start_pos)-1; // find replacement end point (subtract 1 to avoid replacing the < character)
/* if appropriate, get replacement file contents into memory buffer */
if ($use_file) {
$error_string .= $replace_str.' is file to open';
if ($handle = fopen($replace_str,"rb")) {
if ( ($replace_buffer = file_get_contents($replace_str)) === false ) {
// file_get_contents failed on replacement file
$error_string .= 'Failed reading replacement file contents (open was successful);';
} else {
unset ($replace_str);
$replace_str =& $replace_buffer; // alias to avoid unnecessary copying
}
fclose($handle);
} else {
/* fopen failed on replacement file */
$error_string .= 'Failed open of replacement file;';
}
}
$sub_buffer = substr_replace($sub_buffer, $replace_str, $start_pos, $end_pos-$start_pos+1); // length = final - initial + 1
unset ($replace_buffer, $ctrl_tag, $ctrl_attr, $ctrl_attr_val, $sub_tag, $sub_attr, $sub_attr_val, $replace_str);
$ret_val = $sub_buffer; // when loop terminates, return new html to caller
} // end check for subordinate-attribute-value
} // end check for subordinate attribute
} // end check for subordinate tag
} // end check for existence of control attribute and its value in control file
} // end check for existence of control tag in control file
} // get another replacement rule, until there are no more
$error_string .= '-->';
// uncomment this line to turn on crude debugging: $ret_val = $error_string . $ret_val;
}
return $ret_val;
} /* replace_tag_data */
?>
Here are sample contents of a php file that would display a Demonstration Center page, like the page that brought you here. You choose where to divide the responsibility of each component file (e.g., where to put the beginning and ending <body> and <html> tags), how many description files you wish to have, and how many rules you wish to put in each of them.
For serving up web pages faster, this function could be used instead to create static html files, which are then uploaded to the web server.