00001 <?php
00024 class BaseTemplateEngine extends BaseFrameworkSystem {
00030 private $basePath = "";
00031
00035 private $templateType = "html";
00036
00040 private $templateExtension = ".tpl";
00041
00045 private $codeExtension = ".ctp";
00046
00050 private $compileOutputPath = "templates/_compiled";
00051
00055 private $rawTemplateData = "";
00056
00060 private $compiledData = "";
00061
00065 private $lastTemplate = "";
00066
00070 private $varStack = array();
00071
00075 private $loadedTemplates = array();
00076
00080 private $compiledTemplates = array();
00081
00085 private $loadedRawData = null;
00086
00090 private $rawTemplates = null;
00091
00095 private $regExpVarValue = '/([\w_]+)(="([^"]*)"|=([\w_]+))?/';
00096
00102 private $regExpCodeTags = '/\{\?([a-z_]+)(:("[^"]+"|[^?}]+)+)?\?\}/';
00103
00107 private $helpers = array();
00108
00112 private $currGroup = 'general';
00113
00117 private $varGroups = array();
00118
00119
00120 const EXCEPTION_TEMPLATE_TYPE_IS_UNEXPECTED = 0x110;
00121 const EXCEPTION_TEMPLATE_CONTAINS_INVALID_VAR = 0x111;
00122 const EXCEPTION_INVALID_VIEW_HELPER = 0x112;
00123
00130 protected function __construct ($className) {
00131
00132 parent::__construct($className);
00133
00134
00135 $this->removeNumberFormaters();
00136 $this->removeSystemArray();
00137 }
00138
00145 private function isVariableAlreadySet ($var) {
00146
00147 $found = false;
00148
00149
00150 if (isset($this->varStack[$this->currGroup])) {
00151
00152 foreach ($this->varStack[$this->currGroup] as $idx => $currEntry) {
00153
00154
00155 if ($currEntry['name'] == $var) {
00156
00157
00158 $found = $idx;
00159 break;
00160 }
00161 }
00162 }
00163
00164
00165 return $found;
00166 }
00167
00174 protected function readVariable ($var) {
00175
00176 $content = null;
00177
00178
00179 $found = $this->isVariableAlreadySet($var);
00180
00181
00182 if ($found !== false) {
00183
00184 $found = $this->varStack[$this->currGroup][$found]['value'];
00185 }
00186
00187
00188
00189
00190 return $found;
00191 }
00192
00200 private function addVariable ($var, $value) {
00201
00202 $this->setVariableGroup('general');
00203
00204
00205 $this->addGroupVariable($var, $value);
00206 }
00207
00213 private function readCurrentGroup () {
00214
00215 $result = array();
00216
00217
00218 if (isset($this->varStack[$this->currGroup])) {
00219
00220 $result = $this->varStack[$this->currGroup];
00221 }
00222
00223
00224 return $result;
00225 }
00226
00234 public function setVariableGroup ($groupName, $add = true) {
00235
00236
00237 $this->currGroup = $groupName;
00238
00239
00240 if (($groupName != 'general') && ($add === true)) {
00241 $this->varGroups[$groupName] = 'OK';
00242 }
00243 }
00244
00245
00253 public function addGroupVariable ($var, $value) {
00254
00255
00256
00257 $currVars = $this->readCurrentGroup();
00258
00259
00260 $currVars[] = array(
00261 'name' => $var,
00262 'value' => $value
00263 );
00264
00265
00266 $this->varStack[$this->currGroup] = $currVars;
00267 }
00268
00276 private function modifyVariable ($var, $value) {
00277
00278 $idx = $this->isVariableAlreadySet($var);
00279
00280
00281 if ($idx !== false) {
00282
00283 $this->varStack[$this->currGroup][$idx]['value'] = $value;
00284 }
00285 }
00286
00294 private final function setTemplateType ($templateType) {
00295 $this->templateType = (string) $templateType;
00296 }
00297
00304 private final function setLastTemplate ($template) {
00305 $this->lastTemplate = (string) $template;
00306 }
00307
00313 private final function getLastTemplate () {
00314 return $this->lastTemplate;
00315 }
00316
00323 public final function setBasePath ($basePath) {
00324
00325 $this->basePath = (string) $basePath;
00326 }
00327
00333 public final function getBasePath () {
00334
00335 return $this->basePath;
00336 }
00337
00345 public final function setRawTemplateExtension ($templateExtension) {
00346
00347 $this->templateExtension = (string) $templateExtension;
00348 }
00349
00357 public final function setCodeTemplateExtension ($codeExtension) {
00358
00359 $this->codeExtension = (string) $codeExtension;
00360 }
00361
00368 public final function getRawTemplateExtension () {
00369
00370 return $this->templateExtension;
00371 }
00372
00379 public final function getCodeTemplateExtension () {
00380
00381 return $this->codeExtension;
00382 }
00383
00391 public final function setCompileOutputPath ($compileOutputPath) {
00392
00393 $this->compileOutputPath = (string) $compileOutputPath;
00394 }
00395
00401 public final function getTemplateType () {
00402 return $this->templateType;
00403 }
00404
00413 public final function assignVariable ($var, $value) {
00414
00415 $var = trim($var);
00416
00417
00418 if (empty($var)) {
00419
00420 throw new EmptyVariableException(array($this, 'var'), self::EXCEPTION_UNEXPECTED_EMPTY_STRING);
00421 }
00422
00423
00424 $idx = $this->isVariableAlreadySet($var);
00425
00426
00427 if ($idx === false) {
00428
00429
00430 $this->addVariable($var, $value);
00431 } elseif (!empty($value)) {
00432
00433
00434 $this->modifyVariable($var, $value);
00435 }
00436 }
00437
00444 public final function removeVariable ($var) {
00445
00446 $idx = $this->isVariableAlreadySet($var);
00447
00448
00449 if ($idx !== false) {
00450
00451 $this->varStack->offsetUnset($idx);
00452 }
00453 }
00454
00461 protected final function setRawTemplateData ($rawTemplateData) {
00462
00463
00464
00465 $this->rawTemplateData = (string) $rawTemplateData;
00466 }
00467
00473 public final function getRawTemplateData () {
00474
00475 return $this->rawTemplateData;
00476 }
00477
00483 private final function setCompiledData ($compiledData) {
00484
00485
00486 $this->compiledData = (string) $compiledData;
00487 }
00488
00494 public final function getCompiledData () {
00495
00496 return $this->compiledData;
00497 }
00498
00505 private function loadTemplate ($template) {
00506
00507 $ext = $this->getRawTemplateExtension();
00508
00509
00510 if ($this->getTemplateType() == $this->getConfigInstance()->readConfig('code_template_type')) {
00511
00512 $ext = $this->getCodeTemplateExtension();
00513 }
00514
00515
00516 $fqfn = sprintf("%s%s/%s/%s%s",
00517 $this->getBasePath(),
00518 $this->getLanguageInstance()->getLanguageCode(),
00519 $this->getTemplateType(),
00520 (string) $template,
00521 $ext
00522 );
00523
00524
00525 $this->loadRawTemplateData($fqfn);
00526 }
00527
00534 private function loadRawTemplateData ($fqfn) {
00535
00536 $ioInstance = $this->getFileIoInstance();
00537
00538
00539
00540
00541
00542 $rawTemplateData = $ioInstance->loadFileContents($fqfn);
00543
00544
00545 $this->setRawTemplateData($rawTemplateData);
00546
00547
00548 $this->setLastTemplate($fqfn);
00549 }
00550
00559 private function assignTemplateVariable ($varName, $var) {
00560
00561 if ($varName != 'config') {
00562
00563 $this->assignVariable($var, "");
00564 } else {
00565
00566 $this->assignConfigVariable($var);
00567 }
00568 }
00569
00576 private function extractVariablesFromRawData ($rawData) {
00577
00578 $rawData = (string) $rawData;
00579
00580
00581 @preg_match_all('/\$(\w+)(\[(\w+)\])?/', $rawData, $variableMatches);
00582
00583
00584 if ((is_array($variableMatches)) && (count($variableMatches) == 4) && (count($variableMatches[0]) > 0)) {
00585
00586 foreach ($variableMatches[3] as $key => $var) {
00587
00588 $varName = $variableMatches[1][$key];
00589
00590
00591 if (!empty($var)) {
00592
00593 $this->assignTemplateVariable($varName, $var);
00594 }
00595 }
00596 }
00597 }
00598
00613 private function analyzeTemplate (array $templateMatches) {
00614
00615 $backup = $this->getRawTemplateData();
00616
00617
00618 if (is_null($this->loadedRawData)) { $this->loadedRawData = array(); $this->rawTemplates = array(); }
00619
00620
00621 foreach ($templateMatches[1] as $template) {
00622
00623
00624
00625 if ((!isset($this->loadedRawData[$template])) && (!in_array($template, $this->loadedTemplates))) {
00626
00627
00628
00629 if ($this->isVariableAlreadySet($template) !== false) {
00630
00631 $this->loadedRawData[$template] = $this->readVariable($template);
00632
00633
00634 $this->loadedTemplates[] = $template;
00635 } elseif (isset($this->varStack['config'][$template])) {
00636
00637 $this->loadedRawData[$template] = $this->varStack['config'][$template];
00638
00639
00640 $this->loadedTemplates[] = $template;
00641 } else {
00642
00643 try {
00644
00645 $this->loadCodeTemplate($template);
00646 $this->loadedRawData[$template] = $this->getRawTemplateData();
00647
00648
00649
00650 $this->loadedTemplates[] = $template;
00651 } catch (FileNotFoundException $e) {
00652
00653 $this->rawTemplates[] = $template;
00654 } catch (FilePointerNotOpenedException $e) {
00655
00656 $this->rawTemplates[] = $template;
00657 }
00658 }
00659 }
00660 }
00661
00662
00663 $this->setRawTemplateData($backup);
00664 }
00665
00673 private function compileCode ($code, $template) {
00674
00675 if (in_array($template, $this->compiledTemplates)) {
00676
00677 return;
00678 }
00679
00680
00681 $this->compiledTemplates[] = $template;
00682
00683
00684
00685
00686 $backup = $this->getRawTemplateData();
00687
00688
00689 $this->setRawTemplateData($code);
00690
00691
00692 $this->compileTemplate();
00693
00694
00695 $this->loadedRawData[$template] = $this->getRawTemplateData();
00696
00697
00698 $this->setRawTemplateData($backup);
00699 }
00700
00708 private function insertAllTemplates (array $templateMatches) {
00709
00710 foreach ($this->loadedRawData as $template => $code) {
00711
00712
00713 $foundIndex = array_search($template, $templateMatches[1]);
00714
00715
00716 if (($foundIndex !== false) && (isset($templateMatches[0][$foundIndex]))) {
00717
00718
00719 $rawData = $this->getRawTemplateData();
00720
00721
00722 $rawData = str_replace($templateMatches[0][$foundIndex], $code, $rawData);
00723
00724
00725 $this->setRawTemplateData($rawData);
00726 }
00727 }
00728 }
00729
00735 private function loadExtraRawTemplates () {
00736
00737 if (count($this->rawTemplates) > 0) {
00738
00739 foreach ($this->rawTemplates as $key => $template) {
00740 try {
00741
00742 $this->loadWebTemplate($template);
00743
00744
00745 $this->rawTemplates[$template] = $this->getRawTemplateData();
00746
00747
00748 unset($this->rawTemplates[$key]);
00749
00750
00751
00752 $this->loadedTemplates[] = $template;
00753 } catch (FileNotFoundException $e) {
00754
00755 unset($this->rawTemplates[$key]);
00756 } catch (FilePointerNotOpenedException $e) {
00757
00758 unset($this->rawTemplates[$key]);
00759 }
00760 }
00761 }
00762 }
00763
00771 private function assignAllVariables (array $varMatches) {
00772
00773 foreach ($varMatches[1] as $key => $var) {
00774
00775
00776 if (substr($varMatches[2][$key], 0, 1) == "=") {
00777
00778 $varMatches[2][$key] = (string) substr($varMatches[2][$key], 1);
00779 }
00780
00781
00782 if ((substr($varMatches[2][$key], 0, 1) == "\"") && (substr($varMatches[2][$key], -1, 1) == "\"")) {
00783
00784 $this->assignVariable($var, $varMatches[3][$key]);
00785 } elseif (!empty($varMatches[2][$key])) {
00786
00787 die("Deeper analysis not yet implemented!");
00788 }
00789
00790 }
00791 }
00798 private function compileRawTemplateData (array $templateMatches) {
00799
00800 if (count($this->loadedRawData) > 0) {
00801
00802
00803 foreach ($this->loadedRawData as $template => $code) {
00804
00805
00806 if (in_array($template, $this->compiledTemplates)) {
00807
00808 continue;
00809 }
00810
00811
00812 $foundIndex = array_search($template, $templateMatches[1]);
00813
00814
00815 if (($foundIndex !== false) && (isset($templateMatches[3][$foundIndex]))) {
00816
00817
00818 preg_match_all($this->regExpVarValue, $templateMatches[3][$foundIndex], $varMatches);
00819
00820
00821 $this->assignAllVariables($varMatches);
00822
00823 }
00824
00825
00826 $this->compileCode($code, $template);
00827
00828 }
00829
00830
00831 $this->insertAllTemplates($templateMatches);
00832
00833 }
00834 }
00835
00841 private function insertRawTemplates () {
00842
00843 foreach ($this->rawTemplates as $template => $content) {
00844
00845 $this->assignVariable($template, $content);
00846 }
00847 }
00848
00854 private function finalizeVariableCompilation () {
00855
00856 $content = $this->getRawTemplateData();
00857
00858
00859
00860 foreach ($this->varStack['general'] as $currEntry) {
00861
00862
00863
00864 $content = str_replace("\$content[".$currEntry['name']."]", $currEntry['value'], $content);
00865
00866
00867 $content = str_replace("[".$currEntry['name']."]", $currEntry['value'], $content);
00868
00869
00870 $content = str_replace("{?".$currEntry['name']."?}", $currEntry['value'], $content);
00871 }
00872
00873
00874
00875
00876 $this->setRawTemplateData($content);
00877 }
00878
00886 public function loadWebTemplate ($template) {
00887
00888 $this->setTemplateType($this->getConfigInstance()->readConfig('web_template_type'));
00889
00890
00891 $this->loadTemplate($template);
00892 }
00893
00900 public function assignConfigVariable ($var) {
00901
00902
00903 $this->varStack['config'][$var] = $this->getConfigInstance()->readConfig($var);
00904 }
00905
00913 public function loadEmailTemplate ($template) {
00914
00915 $this->setTemplateType($this->getConfigInstance()->readConfig('email_template_type'));
00916
00917
00918 $this->loadTemplate($template);
00919 }
00920
00928 public function loadCodeTemplate ($template) {
00929
00930 $this->setTemplateType($this->getConfigInstance()->readConfig('code_template_type'));
00931
00932
00933 $this->loadTemplate($template);
00934 }
00935
00942 public final function compileVariables () {
00943
00944 $validVar = $this->getConfigInstance()->readConfig('tpl_valid_var');
00945 $dummy = array();
00946
00947
00948 foreach ($this->varStack['general'] as $currVariable) {
00949
00950
00951 $dummy[$currVariable['name']] = $currVariable['value'];
00952 }
00953
00954
00955 $$validVar = $dummy;
00956
00957
00958 $config = null;
00959 if (isset($this->varStack['config'])) {
00960 $config = $this->varStack['config'];
00961 }
00962
00963
00964 unset($idx);
00965 unset($currVariable);
00966
00967
00968 $cnt = 0;
00969 while ($cnt < 3) {
00970
00971 $this->finalizeVariableCompilation();
00972
00973
00974 $eval = sprintf("\$result = \"%s\";",
00975 addslashes($this->getRawTemplateData())
00976 );
00977
00978
00979 while (strpos($eval, "<?php") !== false) {
00980
00981 $evalLeft = substr($eval, 0, strpos($eval, "<?php"));
00982
00983
00984 $evalRight = substr($eval, (strpos($eval, "<?php") + 5));
00985
00986
00987 $evalMiddle = trim(substr($evalRight, 0, strpos($evalRight, "?>")));
00988 $evalMiddle = stripslashes($evalMiddle);
00989
00990
00991 $evalRight = substr($evalRight, (strpos($evalRight, "?>") + 2));
00992
00993
00994 $eval = sprintf("%s<%%php %s %%>%s", $evalLeft, $evalMiddle, $evalRight);
00995 }
00996
00997
00998 $eval = str_replace(
00999 "<%php", "\";",
01000 str_replace(
01001 "%>", "\n\$result .= \"", $eval
01002 )
01003 );
01004
01005
01006 @eval($eval);
01007
01008
01009 if (!isset($result)) {
01010
01011 $this->debugOutput(sprintf("Failed eval() code: <pre>%s</pre>", $this->markupCode($eval, true)), true);
01012
01013
01014 $this->debugBackTrace();
01015 }
01016
01017
01018 $this->setRawTemplateData($result);
01019 $cnt++;
01020 }
01021
01022
01023 $this->finalizeVariableCompilation();
01024
01025
01026 $this->setCompiledData($this->getRawTemplateData());
01027 }
01028
01038 public function compileTemplate () {
01039
01040 if ($this->getTemplateType() != $this->getConfigInstance()->readConfig('code_template_type')) {
01041
01042 throw new UnexpectedTemplateTypeException(array($this, $this->getTemplateType(), $this->getConfigInstance()->readConfig('code_template_type')), self::EXCEPTION_TEMPLATE_TYPE_IS_UNEXPECTED);
01043 }
01044
01045
01046 $rawData = $this->getRawTemplateData();
01047
01048
01049 $rawData = trim(str_replace(" ", " ", $rawData));
01050
01051
01052 $this->extractVariablesFromRawData($rawData);
01053
01054
01055 preg_match_all($this->regExpCodeTags, $rawData, $templateMatches);
01056
01057
01058 if ((is_array($templateMatches)) && (count($templateMatches) == 4) && (count($templateMatches[0]) > 0)) {
01059
01060
01061
01062 $this->analyzeTemplate($templateMatches);
01063
01064
01065 $this->compileRawTemplateData($templateMatches);
01066
01067
01068 $this->loadExtraRawTemplates();
01069
01070
01071 if (count($this->rawTemplates) > 0) {
01072
01073
01074 $this->insertRawTemplates();
01075
01076
01077 $this->setRawTemplateData("");
01078
01079 }
01080
01081 }
01082 }
01083
01091 public function output () {
01092
01093 switch ($this->getTemplateType()) {
01094 case "html":
01095
01096 $this->getWebOutputInstance()->output($this->getCompiledData());
01097 break;
01098
01099 default:
01100
01101 $msg = sprintf("[%s->%s] Unknown/unsupported template type <span class=\"data\">%s</span> detected.",
01102 $this->__toString(),
01103 __FUNCTION__,
01104 $this->getTemplateType()
01105 );
01106
01107
01108 $this->debugOutput($msg);
01109 break;
01110 }
01111 }
01112
01120 protected function loadViewHelper ($helperName) {
01121
01122 $helperName = ucfirst($helperName);
01123
01124
01125 if (!isset($this->helpers[$helperName])) {
01126
01127 $className = "{$helperName}ViewHelper";
01128
01129
01130 if (!class_exists($className)) {
01131
01132 throw new ViewHelperNotFoundException(array($this, $helperName), self::EXCEPTION_INVALID_VIEW_HELPER);
01133 }
01134
01135
01136 $eval = sprintf("\$this->helpers[%s] = %s::create%s();",
01137 $helperName,
01138 $className,
01139 $className
01140 );
01141
01142
01143 eval($eval);
01144 }
01145
01146
01147 return $this->helpers[$helperName];
01148 }
01149
01157 public function assignTemplateWithVariable ($templateName, $variableName) {
01158
01159 $content = $this->getRawTemplateData();
01160
01161
01162 $this->assignVariable($variableName, $content);
01163
01164
01165 $this->setRawTemplateData("");
01166 }
01167
01174 public function transferToResponse (Responseable $responseInstance) {
01175
01176 $responseInstance->writeToBody($this->getCompiledData());
01177 }
01178
01185 public function assignApplicationData (ManageableApplication $appInstance) {
01186
01187 $this->assignVariable('app_full_name' , $appInstance->getAppName());
01188
01189
01190 $this->assignVariable('app_short_name', $appInstance->getAppShortName());
01191
01192
01193 $this->assignVariable('app_version' , $appInstance->getAppVersion());
01194
01195
01196 $appInstance->assignExtraTemplateData($this);
01197 }
01198
01205 public function compileRawCode ($rawCode) {
01206
01207
01208 preg_match_all($this->regExpVarValue, $rawCode, $varMatches);
01209
01210
01211
01212 foreach ($varMatches[0] as $match) {
01213
01214 $varCode = "{?".$match."?}";
01215
01216
01217 if (strpos($rawCode, $varCode) !== false) {
01218
01219
01220 $rawCode = str_replace($varCode, $this->readVariable($match), $rawCode);
01221 }
01222 }
01223
01224
01225 return $rawCode;
01226 }
01227
01233 public final function getVariableGroups () {
01234 return $this->varGroups;
01235 }
01236
01244 public function renameVariable ($oldName, $newName) {
01245
01246
01247 $rawData = $this->getRawTemplateData();
01248
01249
01250 $rawData = str_replace($oldName, $newName, $rawData);
01251
01252
01253 $this->setRawTemplateData($rawData);
01254 }
01255
01263 public final function renderXmlContent ($content = null) {
01264
01265 if (is_null($content)) {
01266
01267 $content = $this->getRawTemplateData();
01268 }
01269
01270
01271 $content = recode("html..utf8", $content);
01272
01273
01274 $xmlParser = xml_parser_create();
01275
01276
01277 xml_parser_set_option($xmlParser, XML_OPTION_CASE_FOLDING, true);
01278
01279
01280 xml_set_object($xmlParser, $this);
01281
01282
01283 xml_set_element_handler($xmlParser, 'startElement', 'endElement');
01284 xml_set_character_data_handler($xmlParser, 'characterHandler');
01285
01286
01287 if (!xml_parse($xmlParser, $content)) {
01288
01289
01290 throw new XmlParserException(array($this, $xmlParser), BaseHelper::EXCEPTION_XML_PARSER_ERROR);
01291 }
01292
01293
01294 xml_parser_free($xmlParser);
01295 }
01296 }
01297
01298
01299 ?>