ILIAS  release_8 Revision v8.19
All Data Structures Namespaces Files Functions Variables Modules Pages
IT.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 // +----------------------------------------------------------------------+
6 // | Copyright (c) 1997-2005 Ulf Wendel, Pierre-Alain Joye |
7 // +----------------------------------------------------------------------+
8 // | This source file is subject to the New BSD license, That is bundled |
9 // | with this package in the file LICENSE, and is available through |
10 // | the world-wide-web at |
11 // | http://www.opensource.org/licenses/bsd-license.php |
12 // | If you did not receive a copy of the new BSD license and are unable |
13 // | to obtain it through the world-wide-web, please send a note to |
14 // | pajoye@php.net, so we can mail you a copy immediately. |
15 // +----------------------------------------------------------------------+
16 // | Author: Ulf Wendel <ulf.wendel@phpdoc.de> |
17 // | Pierre-Alain Joye <pajoye@php.net> |
18 // +----------------------------------------------------------------------+
19 
20 require_once __DIR__ . '/../../exceptions/class.ilTemplateException.php';
21 
98 {
99  public const IT_OK = 1;
100  public const IT_ERROR = -1;
101  public const IT_TPL_NOT_FOUND = -2;
102  public const IT_BLOCK_NOT_FOUND = -3;
103  public const IT_BLOCK_DUPLICATE = -4;
104  public const IT_UNKNOWN_OPTION = -6;
105  public const IT_DEFAULT_BLOCK = '__global__';
106 
110  public array $err = [];
111 
115  public bool $clearCache = false;
116 
120  public string $openingDelimiter = '{';
121 
125  public string $closingDelimiter = '}';
126 
132  public string $blocknameRegExp = '[\.0-9A-Za-z_-]+';
133 
139  public string $variablenameRegExp = '[\.0-9A-Za-z_-]+';
140 
145  public string $variablesRegExp = '';
146 
150  public string $removeVariablesRegExp = '';
151 
155  public bool $removeUnknownVariables = true;
156 
160  public bool $removeEmptyBlocks = true;
161 
165  public string $blockRegExp = '';
166 
170  public string $currentBlock = self::IT_DEFAULT_BLOCK;
171 
175  public string $template = '';
176 
180  public array $blocklist = [];
181 
185  public array $blockdata = [];
186 
190  public array $blockvariables = [];
191 
195  public array $blockparents = [];
196 
200  public array $blockinner = [];
201 
221  public array $touchedBlocks = [];
222 
229  public array $variableCache = [];
230 
237  public bool $clearCacheOnParse = false;
238 
243  public string $fileRoot = '';
244 
248  public bool $flagBlocktrouble = false;
249 
253  public bool $flagGlobalParsed = false;
254 
264  public bool $flagCacheTemplatefile = true;
265 
269  public string $lastTemplatefile = '';
270 
279  public array $_options = [
280  'preserve_data' => false,
281  'use_preg' => true
282  ];
283 
287  protected string $real_filename = '';
288 
298  public function __construct(string $root = '', array $options = null)
299  {
300  if (!is_null($options)) {
301  $this->setOptions($options);
302  }
303  $this->variablesRegExp = '@' . $this->openingDelimiter .
304  '(' . $this->variablenameRegExp . ')' .
305  $this->closingDelimiter . '@sm';
306  $this->removeVariablesRegExp = '@' . $this->openingDelimiter .
307  "\s*(" . $this->variablenameRegExp .
308  ")\s*" . $this->closingDelimiter . '@sm';
309 
310  $this->blockRegExp = '@<!--\s+BEGIN\s+(' . $this->blocknameRegExp .
311  ')\s+-->(.*)<!--\s+END\s+\1\s+-->@sm';
312 
313  $this->setRoot($root);
314  }
315 
321  public function setOption(string $option, $value): int
322  {
323  if (array_key_exists($option, $this->_options)) {
324  $this->_options[$option] = $value;
325  return self::IT_OK;
326  }
327 
328  throw new ilTemplateException($this->errorMessage(self::IT_UNKNOWN_OPTION) . ": '$option'");
329  }
330 
336  public function setOptions(array $options): int
337  {
338  foreach ($options as $option => $value) {
339  $this->setOption($option, $value);
340  }
341 
342  return self::IT_OK;
343  }
344 
349  public function show(string $block = self::IT_DEFAULT_BLOCK): void
350  {
351  print $this->get($block);
352  }
353 
358  public function get(string $block = self::IT_DEFAULT_BLOCK): string
359  {
360  if ($block === self::IT_DEFAULT_BLOCK && !$this->flagGlobalParsed) {
361  $this->parse();
362  }
363 
364  if (!isset($this->blocklist[$block])) {
365  throw new ilTemplateException($this->errorMessage(self::IT_BLOCK_NOT_FOUND) . '"' . $block . "'");
366  }
367 
368  if (isset($this->blockdata[$block])) {
369  $ret = $this->blockdata[$block];
370  if ($this->clearCache) {
371  unset($this->blockdata[$block]);
372  }
373  if ($this->_options['preserve_data']) {
374  $ret = str_replace(
375  $this->openingDelimiter .
376  '%preserved%' . $this->closingDelimiter,
377  $this->openingDelimiter,
378  $ret
379  );
380  }
381  return $ret;
382  }
383 
384  return '';
385  }
386 
394  public function parse(string $block = self::IT_DEFAULT_BLOCK, bool $flag_recursion = false): bool
395  {
396  static $regs, $values;
397 
398  if (!isset($this->blocklist[$block])) {
399  throw new ilTemplateException($this->errorMessage(self::IT_BLOCK_NOT_FOUND) . '"' . $block . "'");
400  }
401 
402  if (self::IT_DEFAULT_BLOCK === $block) {
403  $this->flagGlobalParsed = true;
404  }
405 
406  if (!$flag_recursion) {
407  $regs = [];
408  $values = [];
409  }
410  $outer = $this->blocklist[$block];
411  $empty = true;
412 
413  if ($this->clearCacheOnParse) {
414  foreach ($this->variableCache as $name => $value) {
415  $regs[] = $this->openingDelimiter .
417  $values[] = $value;
418  $empty = false;
419  }
420  $this->variableCache = [];
421  } else {
422  foreach ($this->blockvariables[$block] as $allowedvar => $v) {
423  if (isset($this->variableCache[$allowedvar])) {
424  $regs[] = $this->openingDelimiter .
425  $allowedvar . $this->closingDelimiter;
426  $values[] = $this->variableCache[$allowedvar];
427  unset($this->variableCache[$allowedvar]);
428  $empty = false;
429  }
430  }
431  }
432 
433  if (isset($this->blockinner[$block])) {
434  foreach ($this->blockinner[$block] as $k => $innerblock) {
435  $this->parse($innerblock, true);
436  if ($this->blockdata[$innerblock] !== '') {
437  $empty = false;
438  }
439 
440  $placeholder = $this->openingDelimiter . "__" .
441  $innerblock . "__" . $this->closingDelimiter;
442  $outer = str_replace(
443  $placeholder,
444  $this->blockdata[$innerblock],
445  $outer
446  );
447  $this->blockdata[$innerblock] = "";
448  }
449  }
450 
451  if (!$flag_recursion && 0 !== count($values)) {
452  if ($this->_options['use_preg']) {
453  $regs = array_map(
454  [
455  &$this,
456  '_addPregDelimiters'
457  ],
458  $regs
459  );
460  $funcReplace = 'preg_replace';
461  } else {
462  $funcReplace = 'str_replace';
463  }
464 
465  if ($this->_options['preserve_data']) {
466  $values = array_map(
467  [&$this, '_preserveOpeningDelimiter'],
468  $values
469  );
470  }
471 
472  $outer = $funcReplace($regs, $values, $outer);
473 
474  if ($this->removeUnknownVariables) {
475  $outer = preg_replace($this->removeVariablesRegExp, "", $outer);
476  }
477  }
478 
479  if ($empty) {
480  if (!$this->removeEmptyBlocks) {
481  $this->blockdata[$block] .= $outer;
482  } elseif (isset($this->touchedBlocks[$block])) {
483  $this->blockdata[$block] .= $outer;
484  unset($this->touchedBlocks[$block]);
485  }
486  } elseif (empty($this->blockdata[$block])) {
487  $this->blockdata[$block] = $outer;
488  } else {
489  $this->blockdata[$block] .= $outer;
490  }
491 
492  return $empty;
493  }
494 
499  public function parseCurrentBlock(): bool
500  {
501  return $this->parse($this->currentBlock);
502  }
503 
514  public function setVariable($variable, $value = ''): void
515  {
516  if (is_array($variable)) {
517  $this->variableCache = array_merge(
518  $this->variableCache,
519  $variable
520  );
521  } else {
522  $this->variableCache[$variable] = $value;
523  }
524  }
525 
531  public function setCurrentBlock(string $block = self::IT_DEFAULT_BLOCK): bool
532  {
533  if (!isset($this->blocklist[$block])) {
534  throw new ilTemplateException($this->errorMessage(self::IT_BLOCK_NOT_FOUND) . '"' . $block . "'");
535  }
536 
537  $this->currentBlock = $block;
538 
539  return true;
540  }
541 
546  public function touchBlock(string $block): bool
547  {
548  if (!isset($this->blocklist[$block])) {
549  throw new ilTemplateException($this->errorMessage(self::IT_BLOCK_NOT_FOUND) . '"' . $block . "'");
550  }
551 
552  $this->touchedBlocks[$block] = true;
553 
554  return true;
555  }
556 
564  protected function init(): void
565  {
566  $this->free();
568 
569  if ($blockdata = $blocks->get($this->real_filename)) {
570  $this->blockdata = $blockdata['blockdata'];
571  $this->blocklist = $blockdata['blocklist'];
572  } else {
574  $this->findBlocks($this->template);
575  $blockdata['blockdata'] = $this->blockdata;
576  $blockdata['blocklist'] = $this->blocklist;
577  $blocks->set($this->real_filename, $blockdata, 60);
578  }
579 
580  // we don't need it any more
581  $this->template = '';
582 
584  if ($blockvariables = $variables->get($this->real_filename)) {
585  $this->blockvariables = $blockvariables;
586  } else {
587  $this->buildBlockvariablelist();
588  $variables->set($this->real_filename, $this->blockvariables, 60);
589  }
590  }
591 
596  public function free(): void
597  {
598  $this->err = [];
599 
600  $this->currentBlock = self::IT_DEFAULT_BLOCK;
601 
602  $this->variableCache = [];
603  $this->blocklist = [];
604  $this->touchedBlocks = [];
605 
606  $this->flagBlocktrouble = false;
607  $this->flagGlobalParsed = false;
608  }
609 
616  public function setTemplate(
617  string $template,
618  bool $removeUnknownVariables = true,
619  bool $removeEmptyBlocks = true
620  ): bool {
621  $this->removeUnknownVariables = $removeUnknownVariables;
622  $this->removeEmptyBlocks = $removeEmptyBlocks;
623 
624  if ($template === '' && $this->flagCacheTemplatefile) {
625  $this->variableCache = [];
626  $this->blockdata = [];
627  $this->touchedBlocks = [];
628  $this->currentBlock = self::IT_DEFAULT_BLOCK;
629  } else {
630  $this->template =
631  '<!-- BEGIN ' . self::IT_DEFAULT_BLOCK . ' -->' .
632  $template .
633  '<!-- END ' . self::IT_DEFAULT_BLOCK . ' -->';
634  $this->init();
635  }
636 
637  if ($this->flagBlocktrouble) {
638  return false;
639  }
640 
641  return true;
642  }
643 
648  public function loadTemplatefile(
649  string $filename,
650  bool $removeUnknownVariables = true,
651  bool $removeEmptyBlocks = true
652  ): bool {
653  $template = '';
654  if (!$this->flagCacheTemplatefile ||
655  $this->lastTemplatefile !== $filename
656  ) {
657  $template = $this->getFile($filename);
658  }
659  $this->lastTemplatefile = $filename;
660 
661  return $template !== '' && $this->setTemplate(
662  $template,
663  $removeUnknownVariables,
664  $removeEmptyBlocks
665  );
666  }
667 
674  public function setRoot(string $root): void
675  {
676  if ($root !== '' && substr($root, -1) !== '/') {
677  $root .= '/';
678  }
679 
680  $this->fileRoot = $root;
681  }
682 
686  public function buildBlockvariablelist(): void
687  {
688  foreach ($this->blocklist as $name => $content) {
689  preg_match_all($this->variablesRegExp, $content, $regs);
690 
691  if (count($regs[1]) !== 0) {
692  foreach ($regs[1] as $var) {
693  $this->blockvariables[$name][$var] = true;
694  }
695  } else {
696  $this->blockvariables[$name] = [];
697  }
698  }
699  }
700 
705  public function findBlocks(string $string): array
706  {
707  $blocklist = [];
708  if (preg_match_all($this->blockRegExp, $string, $regs, PREG_SET_ORDER)) {
709  foreach ($regs as $match) {
710  $blockname = $match[1];
711  $blockcontent = $match[2];
712 
713  if (isset($this->blocklist[$blockname])) {
714  throw new ilTemplateException($this->errorMessage(self::IT_BLOCK_DUPLICATE, $blockname));
715  }
716 
717  $this->blocklist[$blockname] = $blockcontent;
718  $this->blockdata[$blockname] = "";
719 
720  $blocklist[] = $blockname;
721 
722  $inner = $this->findBlocks($blockcontent);
723  foreach ($inner as $name) {
724  $pattern = sprintf(
725  '@<!--\s+BEGIN\s+%s\s+-->(.*)<!--\s+END\s+%s\s+-->@sm',
726  $name,
727  $name
728  );
729 
730  $this->blocklist[$blockname] = preg_replace(
731  $pattern,
732  $this->openingDelimiter .
733  '__' . $name . '__' .
734  $this->closingDelimiter,
735  $this->blocklist[$blockname]
736  );
737  $this->blockinner[$blockname][] = $name;
738  $this->blockparents[$name] = $blockname;
739  }
740  }
741  }
742 
743  return $blocklist;
744  }
745 
750  public function getFile(string $filename): string
751  {
752  if ($filename[0] === '/' && substr($this->fileRoot, -1) === '/') {
753  $filename = substr($filename, 1);
754  }
755 
756  $filename = $this->fileRoot . $filename;
757 
758  $this->real_filename = $filename;
760  if (!$content = $ilGlobalCache->get($filename)) {
761  if (!($fh = @fopen($filename, 'rb'))) {
762  throw new ilTemplateException($this->errorMessage(self::IT_TPL_NOT_FOUND) . ': "' . $filename . '"');
763  }
764 
765  $fsize = filesize($filename);
766  if ($fsize < 1) {
767  fclose($fh);
768  return '';
769  }
770 
771  $content = fread($fh, $fsize);
772  $ilGlobalCache->set($filename, $content, 60);
773  fclose($fh);
774  }
775 
776  return $content;
777  }
778 
783  public function _addPregDelimiters(string $str): string
784  {
785  return '@' . $str . '@';
786  }
787 
791  public function _preserveOpeningDelimiter(string $str): string
792  {
793  return (false === strpos($str, $this->openingDelimiter)) ?
794  $str :
795  str_replace(
796  $this->openingDelimiter,
797  $this->openingDelimiter .
798  '%preserved%' . $this->closingDelimiter,
799  $str
800  );
801  }
802 
806  public function errorMessage(int $value, string $blockname = ''): string
807  {
808  static $errorMessages;
809  if (!isset($errorMessages)) {
810  $errorMessages = [
811  self::IT_OK => '',
812  self::IT_ERROR => 'unknown error',
813  self::IT_TPL_NOT_FOUND => 'Cannot read the template file',
814  self::IT_BLOCK_NOT_FOUND => 'Cannot find this block',
815  self::IT_BLOCK_DUPLICATE => 'The name of a block must be' .
816  ' uniquewithin a template.' .
817  ' Found "' . $blockname . '" twice.' .
818  'Unpredictable results ' .
819  'may appear.',
820  self::IT_UNKNOWN_OPTION => 'Unknown option'
821  ];
822  }
823 
824  return $errorMessages[$value] ?? $errorMessages[self::IT_ERROR];
825  }
826 }
string $openingDelimiter
First character of a variable placeholder ( _{_VARIABLE} ).
Definition: IT.php:120
string $closingDelimiter
Last character of a variable placeholder ( {VARIABLE_}_ ).
Definition: IT.php:125
const IT_BLOCK_DUPLICATE
Definition: IT.php:103
const IT_OK
Definition: IT.php:99
setOptions(array $options)
Sets the options for the template class.
Definition: IT.php:336
setTemplate(string $template, bool $removeUnknownVariables=true, bool $removeEmptyBlocks=true)
Sets the template.
Definition: IT.php:616
bool $removeEmptyBlocks
Controls the handling of empty blocks, default is remove.
Definition: IT.php:160
const IT_UNKNOWN_OPTION
Definition: IT.php:104
string $currentBlock
Name of the current block.
Definition: IT.php:170
array $_options
$_options[&#39;preserve_data&#39;] Whether to substitute variables and remove empty placeholders in data pass...
Definition: IT.php:279
string $template
Content of the template.
Definition: IT.php:175
array $blockparents
Array of block parents.
Definition: IT.php:195
array $blockvariables
Array of variables in a block.
Definition: IT.php:190
string $variablesRegExp
RegExp used to find variable placeholder, filled by the constructor.
Definition: IT.php:145
const IT_DEFAULT_BLOCK
Definition: IT.php:105
bool $clearCacheOnParse
Clear the variable cache on parse? If you&#39;re not an expert just leave the default false...
Definition: IT.php:237
static log(string $message, int $log_level)
string $removeVariablesRegExp
RegExp used to strip unused variable placeholder.
Definition: IT.php:150
bool $flagCacheTemplatefile
EXPERIMENTAL! FIXME! Flag indication that a template gets cached.
Definition: IT.php:264
string $fileRoot
Root directory for all file operations.
Definition: IT.php:243
bool $flagBlocktrouble
Internal flag indicating that a blockname was used multiple times.
Definition: IT.php:248
bool $removeUnknownVariables
Controls the handling of unknown variables, default is remove.
Definition: IT.php:155
findBlocks(string $string)
Recusively builds a list of all blocks within the template.
Definition: IT.php:705
free()
Clears all datafields of the object.
Definition: IT.php:596
string $blocknameRegExp
RegExp matching a block in the template.
Definition: IT.php:132
setOption(string $option, $value)
Sets the option for the template class.
Definition: IT.php:321
if($format !==null) $name
Definition: metadata.php:247
string $lastTemplatefile
EXPERIMENTAL! FIXME!
Definition: IT.php:269
setVariable($variable, $value='')
Sets a variable value.
Definition: IT.php:514
show(string $block=self::IT_DEFAULT_BLOCK)
Print a certain block with all replacements done.
Definition: IT.php:349
bool $flagGlobalParsed
Flag indicating that the global block was parsed.
Definition: IT.php:253
getFile(string $filename)
Reads a file from disk and returns its content.
Definition: IT.php:750
touchBlock(string $block)
Preserves an empty block even if removeEmptyBlocks is true.
Definition: IT.php:546
string $blockRegExp
RegExp used to find blocks an their content, filled by the constructor.
Definition: IT.php:165
_addPregDelimiters(string $str)
Adds delimiters to a string, so it can be used as a pattern in preg_* functions.
Definition: IT.php:783
errorMessage(int $value, string $blockname='')
Return a textual error message for a IT error code.
Definition: IT.php:806
Integrated Template - IT Well there&#39;s not much to say about it.
Definition: IT.php:97
array string $real_filename
Holds the real template file name.
Definition: IT.php:287
_preserveOpeningDelimiter(string $str)
Replaces an opening delimiter by a special string.
Definition: IT.php:791
const IT_TPL_NOT_FOUND
Definition: IT.php:101
bool $clearCache
Clear cache on get()?
Definition: IT.php:115
$filename
Definition: buildRTE.php:78
parseCurrentBlock()
Parses the current block.
Definition: IT.php:499
buildBlockvariablelist()
Build a list of all variables within of a block.
Definition: IT.php:686
setCurrentBlock(string $block=self::IT_DEFAULT_BLOCK)
Sets the name of the current block that is the block where variables are added.
Definition: IT.php:531
const IT_BLOCK_NOT_FOUND
Definition: IT.php:102
setRoot(string $root)
Sets the file root.
Definition: IT.php:674
const IT_ERROR
Definition: IT.php:100
string $variablenameRegExp
RegExp matching a variable placeholder in the template.
Definition: IT.php:139
array $touchedBlocks
List of blocks to preverse even if they are "empty".
Definition: IT.php:221
init()
Clears all datafields of the object and rebuild the internal blocklist LoadTemplatefile() and setTemp...
Definition: IT.php:564
loadTemplatefile(string $filename, bool $removeUnknownVariables=true, bool $removeEmptyBlocks=true)
Reads a template file from the disk.
Definition: IT.php:648
parse(string $block=self::IT_DEFAULT_BLOCK, bool $flag_recursion=false)
Parses the given block.
Definition: IT.php:394
array $blockinner
Array of inner blocks of a block.
Definition: IT.php:200
array $err
Contains the error objects.
Definition: IT.php:110
array $blockdata
Array with the parsed content of a block.
Definition: IT.php:185
array $variableCache
Variable cache.
Definition: IT.php:229
static getInstance(?string $component)
array $blocklist
Array of all blocks and their content.
Definition: IT.php:180
__construct(string $root='', array $options=null)
Builds some complex regular expressions and optinally sets the file root directory.
Definition: IT.php:298