/ *** Template class. By Nathan Codding of the phpBB group. * The interface was originally inspired by PHPLib templates, * and the template file formats are quite similar. ** / class Template {var $ classname = "Template" ; // Variable That Holds All The Data We'll Be Substituting Into // THIS WILL End Up Being a Multi-Dimensional Array Like this: // $ this -> _ TPLData [Block [ipil.] [ipild.] [ipild2.] [ipirablename] == value // if it's a root-level variable, it'll be like this: // $ this- > _tpldata [.] [0] [varName] == Value var $ _tpldata = array ();
// Hash of FileNames for Each Template Handle. Var $ files = array (); // root template Directory. Var $ root = ""; // this will hash handle name. Var $ compiled_code = Array (); // this will hold the uncompiled code for what handle. var $ uncompiled_code = array (); / ** * Constructor. Simply sets the root dir. * / function template ($ root = ") { $ this-> set_rootdir ($ root);} / ** * destroys this template object. SHOULD BE CALED WHEN You're Done with it, in Order * to clear out the template data so you can load / parse a new template set * / Function destroy () {$ this -> _ TPLDATA = array ();} / ** * sets the template root directory for this template object. * / Function set_rootdir ($ dir) {if (! Is_dir ($ dir) ) { return false;} $ this-> root = $ dir; return true;..} / ** * Sets the template filenames for handles $ filename_array * should be a hash of handle => filename pairs * / function set_filenames ($ filename_array) {IF ($ filename_array) {Return False;} Reset ($ filename_array); while ($ Handle, $ filename) = Each ($ filename_array) {$ this-> files [$ hand] = $ This-> make_filename ($ filename);
} RETURN TRUE;} / ** * Load the file for the hand, compile. This will print out * The results of execut out * the results of executing the template. * / Function pparse ($ hand) {ix () {ix $ this-> loadingfile ($ hand)) {Die ("Template-> PPARSE (): COULDN't Load Template File for Handle $ Handle");} // actually compile the template now. if (! isset ($ this-> compiled_code [$ HANDLE]) || EMPTY ($ HANDLE)) {// Actually Compile The Code Now. $ this-> compiled_code [$ hand] = $ this-> compile ($ This-> uncompiled_code [$ hand]);} // Run the compiled code. Eval ($ Handle]); return true;} / ** * Inserts the uncompiled code for $ handle as the uncompiled code for $ handle as the * va lue of $ varname in the root-level. This can be used * to effectively include a template in the middle of another * template. * Note that all desired assignments to the variables in $ handle should be done * BEFORE calling this function. * / Function assign_var_from_handle ($ Varname, $ Handle) {if (! $ This-> loadfile ($ hand) {die ("template-> assign_var_from_handle (): couldn't load template file for handle $ handle");} / / Compile it, with the "no echo statements" option on. $ _STR = ""
$ code = $ this-> compile ($ Handle], True, '_STR'); // Evaluate The Variable Assignment. EVAL ($ code); // Assign THE VALUE OF THE generated variable to the meaning given varname $ this-> assign_var ($ varname, $ _str);. return true;..} / ** * Block-level variable assignment Adds a new block iteration with the given * variable assignments Note that this should only be called once . ( '.' strstr ($ blockname,)) per block * iteration * / function assign_block_vars ($ blockname, $ vararray) {if {// Nested block $ blocks = explode (, $ blockname '.');. $ blockcount = SizeOf ($ Blocks) - 1; $ Str = '$ this -> _ TPLData'; for ($ I = 0; $ I <$ blockcount; $ I ) {$ Str. = '[' '. $ B LOCKS [$ I]. '.'] '; EVAL (' $ lastiteration = sizeof ('. $ str.') - 1; '); $ Str. =' ['. $ lastiteration.'] ';} / / NOW WE Add The Block That We're ASSIGNING TO. // We're Adding A New Iteration To this Block with the given // Variable Assignments. $ Str. = '[' '. $ Blocks [$ blockcount]. '.'] [] = $ Varray; ';
// Now We Evaluate this assignment We've Built Up. EVAL ($ STR);} else {// Top-Level Block. // Add a new ity.. -> _ tpldata [$ blockname. '.'] [] = $ vararray;} return true;} / ** * Root-level variable assignment Adds to current assignments, overriding * any existing variable assignment with the same name * /.. Function assign_vars ($ VARARRAY) {RESET ($ VaRray); while ($ key, $ val) = Each ($ zararray)) {$ this -> _ tpldata ['.'] [0] [$ key] = $ Val;} Return True;} / ** * root-level variable assignment. Adds to current assignments, Overriding * Any EXISTING VARIA BLE Assignment with the Same Name. * / Function Assign_var ($ VarName, $ VARVAL) {$ this -> _ TPLData ['.'] [0] [$ VarName] = $ VARVAL; RETURN TRUE;} / ** * generates a full path filename for the given filename, which can either * be an absolute name, or a name relative to the rootdir for this Template * object. * / function make_filename ($ filename) {// Check if it's an absolute or relative path IF (Substr ($ FileName, 0, 1)! = '/') {$ Filename = $ this-> root. '/'
. $ Filename;} f (! File_exists ($ filename)) {Die ("Template-> make_filename (): error - file $ filename does not exist");} Return $ filename;} / ** * if not already DONE , load the file for the given handle and populate * the uncompiled_code [] hash with its code. do not compile. * / function loadfile ($ handle) {// If the file for this handle is already loaded and compiled, do nothing. IF ($ HANDLE) &&! EMPTY ($ this-> uncompiled_code [$ hand)) {return true;} // if we don't have a file assist to this handle, Die IF (! $ HANDLE)) {Die ("Template-> LoadFile (): No file specified for handle $ hand"); $ filename = $ this-> Files [$ Handle]; $ Str = IMPLODE ("", @file ($ STR)) {Die ("Template-> LoadFile (): FILE $ FileName For Handle $ Handle Is Empty ");} $ This-> uncompiled_code [$ hand] = $ str; return true;} / ** * compiles the given string of code, and returns * the result in a string. * If" do_not_echo "
is true, the returned code will not be directly * executable, but can be used as part of a variable assignment * for use in assign_code_from_handle (). * / function compile ($ code, $ do_not_echo = false, $ retvar = '') {// replace with / and then 'with'. $ Code = str_replace ('/', '', $ code); $ code = str_replace ('', '/' ', $ code); // change template Varrefs INTO PHP VARREFS // This One Will Handle Varrefs with Namespaces $ VARREFS = array (); preg_match_all ('# {(([A-Z0-9 -_] ?) ?) ([A-Z0-9 -_] ?)} # is', $ code, $ varrefs; $ varcount = sizeof ($ VARREFS [1]); for ($ I = 0; $ 11 $ VARCOUNT; $ I ) {$ namespace = $ VARREFS [1] [$ I]; $ VarName = $ VARREFS [$ I]; $ new = $ this-> generate_block_varref ($ namespace, $ varname); $ code = str_rep LACE ($ VARREFS [0] [$ I], $ New, $ CODE);} // this will handle the remaining root-level varRefs $ code = preg_replace ('# {([A-Z0-9 -_] * ?)} # is ',' '. ($ this -> _ tpldata ['. '] [0] [' 1 ']))? $ this -> _ tpldata ['. '] [0] [' 1 ']:' ').' ', $ Code); // Break IT Up Into lines. $ Code_lines = expenented; $ block_nesting_level = 0; $ block_names = array (); $ block_names [0] = "."
// Second: prepend echo ', append'. "N"; to each line. $ Line_count = sizeof ($ code_lines); for ($ I = 0; $ i <$ line_count; $ i ) {$ code_lines [$ i ] = CHOP ($ CODE_LINES [$ I]); if (preg_match ('#! - begin (. *?) -> #', $ code_lines [$ i], $ m)) {$ n [0 ] = $ M [0]; $ n [1] = $ m [1]; // added: dougk_ff7-keeps templates from bombing if begin is on the same line as end .. I think. If (preg_match " # ', $ n)) {$ block_nesting_level ; $ block_names [$ block_nesting_level] = $ m [1]; if ($ block_nesting_level <2 ) { // block is not nested. $ Code_lines [$ i] = '$ _'. $ A [1]. '_Count = (isset ($ this -> _ tpldata [' '. $ N [1].'. '] ))? SizeOf ($ this -> _ TPLDATA [''. $ N [1]. '.']): 0; '; $ code_lines [$ i]. = "N".' For ($ _ '. $ N [1]. '_i = 0; $ _'. $ n [1]. '_i <$ _'. $ n [1]. '_count; $ _'. $ n [1]. '_i )' ; $ Code_lines [$ I]. = "N". '{';
} Else {// This block is nested // Generate a namespace string for this block $ namespace = implode (, $ block_names '.');.. // strip leading period from root level .. $ namespace = substr ($ namespace , 2); // Get a reference to the data array for this block that depends on the // current indices of all parent blocks $ varref = $ this-> generate_block_data_ref ($ namespace, false);. // Create the for loop Code to Iterate Over this block. $ code_lines [$ i] = '$ _' . $ A [1]. '_Count = (isset ('. $ VarRef. '))? SizeOf ('. $ VarRef. '): 0;'; $ code_lines [$ I]. = "N". 'For ($ _ '. $ N [1].' _I = 0; $ _ '. $ N [1].' _I <$ _ '. $ N [1].' _Count; $ _ '. $ N [1 ]. '_I )'; $ code_lines [$ I]. = "N". '{';} // We Have The end of a block. Unset ($ block_names [$ block_nesting_level]);
$ block_nesting_level ---; $ code_lines [$ I]. = '} // end'. $ n [1]; $ m [0] = $ n [0]; $ m [1] = $ n [1]; } else {// We have the start of a block $ block_nesting_level ;. $ block_names [$ block_nesting_level] = $ m [1];. if ($ block_nesting_level <2) {// Block is not nested $ code_lines [$ i] = '$ _'. $ M [1]. '_Count = ($ this -> _ tpldata [' '. $ M [1].'. '])) SIZEOF ($ this -> _ TPLData [' ' $ M [1]. '.']): 0; '; $ code_lines [$ i]. = "N".' For ($ _ '. $ M [1].' _I = 0; _ '. $ M [1].' _I <$ _ '. $ M [1].' _Count; $ _ '. $ M [1].' _I ) '; $ code_lines [$ i]. = "N ". '{';} Else {// this block is nested. // generate a namespace string for this block. $ Namespace = @ =. '
, $ Block_names); // strip leading period from root level .. $ namespace = substr ($ namespace, 2); // Get a reference to the data array for this block that depends on the // current indices of all parent blocks . $ VarRef = $ this-> generate_block_data_ref ($ namespace, false); // Create the for loop code to iterate over this block. $ Code_lines [$ i] = '$ _'. $ M [1]. '_Count = (Isset ('. $ VarRef.')))? SizeOf ('. $ VarRef.'): 0; '; $ code_lines [$ I]. = "N".' For ($ _ '. $ M [1] . '_I = 0; $ M [1].' _I <$ _ '. $ M [1].' _Count; $ _ '. $ M [1].' _I ) '; $ code_lines [ $ I]. = "n". '{'; }}} Else if (preg_match ('# #', $ code_lines [$ i], $ m)) {// We Have The end of a block. Unset $ block_names [$ block_nesting_level]); $ block_nesting_level ---; $ code_lines [$ i] = '} // end'. $ m [1];
} Else {// We Have An ORDINARY LINE OF CODE. IF ($ do_not_echo) {$ code_lines [$ I] = 'echo' '. $ Code_lines [$ I].' '. "/ N";';} Else {$ Code_Lines [$ I] = '$'. $ RETVAR. '. =' '. $ code_lines [$ I].' ''. "/ n"; ';}}} // BRING IT Back Into A Single string of lines of code $ code = implode ( "n", $ code_lines);. return $ code;.} / ** * Generates a reference to the given variable inside the given (possibly nested) * block namespace This is a string Of the form: * '. $ this -> _ tpldata [' Parent '] [$ _ parent_i] [' $ Child1 '] [$ _ child1_i] [' $ Chi LD2 '] [$ _ child2_i] ... [' varname '].' * it's ready to be inserted Into an "echo" line in one of the templates. * Namespace. * / function generate_block_varref ($ namespace, $ varname) {// Strip the trailing period $ namespace = substr ($ namespace, 0, strlen ($ namespace) - 1);. // Get a reference to the data block for this namespace $. Varref = $ this-> generate_block_data_ref ($ namespace, true);
// Prepend The next the next to stick thing............................................................................................................. $ VARREF. '))?'. $ varRef. ':' ').' '; returnid;} / ** * generates a reference to the array of data value for the given * (Possibly Nested) block namespace. This is a string of the form: * $ this -> _ tpldata ['Parent'] [$ _ parent_i] ['$ child1'] [$ _ child1_i] ['$ child2'] [$ _ child2_i] ... [$ childn '] * * If $ include_last_iterator is true, then [$ _childN_i] will be appended to the form shown above * NOTE:. ".". does not expect a trailing on the blockname * / function generate_block_data_ref ($ blockname, $ include_last_iterator) {// Get An Array of The Blocks Involved. $ Blocks INVED (".", $ Blockname); $ blockcount = sizeof ($ blocks) - 1; $ VARREF = '$ this -> _ TPLDATA'; // build up The string with everything but the last child. for ($ I = 0; $ I <$ blockcount; $ i ) {$ VARREF. = '[' '. $ Blocks [$ I]. '.'] [$ _ '. $ blocks [$ I].' _i] ';} // add the block reference for the last child. $ varRef. =' [''. $ Blocks [$ blockcount]. '.'] ';