ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
IT.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
21 // +----------------------------------------------------------------------+
22 // | Copyright (c) 1997-2005 Ulf Wendel, Pierre-Alain Joye |
23 // +----------------------------------------------------------------------+
24 // | This source file is subject to the New BSD license, That is bundled |
25 // | with this package in the file LICENSE, and is available through |
26 // | the world-wide-web at |
27 // | http://www.opensource.org/licenses/bsd-license.php |
28 // | If you did not receive a copy of the new BSD license and are unable |
29 // | to obtain it through the world-wide-web, please send a note to |
30 // | pajoye@php.net, so we can mail you a copy immediately. |
31 // +----------------------------------------------------------------------+
32 // | Author: Ulf Wendel <ulf.wendel@phpdoc.de> |
33 // | Pierre-Alain Joye <pajoye@php.net> |
34 // +----------------------------------------------------------------------+
42 
43 require_once __DIR__ . '/../../exceptions/class.ilTemplateException.php';
44 
121 {
122  public const IT_OK = 1;
123  public const IT_ERROR = -1;
124  public const IT_TPL_NOT_FOUND = -2;
125  public const IT_BLOCK_NOT_FOUND = -3;
126  public const IT_BLOCK_DUPLICATE = -4;
127  public const IT_UNKNOWN_OPTION = -6;
128  public const IT_DEFAULT_BLOCK = '__global__';
129  private \ILIAS\Cache\Container\Container $block_cache;
130  private \ILIAS\Cache\Container\Container $variable_cache;
131  private \ILIAS\Cache\Container\Container $template_cache;
132 
136  public array $err = [];
137 
141  public bool $clearCache = false;
142 
146  public string $openingDelimiter = '{';
147 
151  public string $closingDelimiter = '}';
152 
158  public string $blocknameRegExp = '[\.0-9A-Za-z_-]+';
159 
165  public string $variablenameRegExp = '[\.0-9A-Za-z_-]+';
166 
171  public string $variablesRegExp = '';
172 
176  public string $removeVariablesRegExp = '';
177 
181  public bool $removeUnknownVariables = true;
182 
186  public bool $removeEmptyBlocks = true;
187 
191  public string $blockRegExp = '';
192 
196  public string $currentBlock = self::IT_DEFAULT_BLOCK;
197 
201  public string $template = '';
202 
206  public array $blocklist = [];
207 
211  public array $blockdata = [];
212 
216  public array $blockvariables = [];
217 
221  public array $blockparents = [];
222 
226  public array $blockinner = [];
227 
247  public array $touchedBlocks = [];
248 
255  public array $variableCache = [];
256 
263  public bool $clearCacheOnParse = false;
264 
269  public string $fileRoot = '';
270 
274  public bool $flagBlocktrouble = false;
275 
279  public bool $flagGlobalParsed = false;
280 
290  public bool $flagCacheTemplatefile = true;
291 
295  public string $lastTemplatefile = '';
296 
305  public array $_options = [
306  'preserve_data' => false,
307  'use_preg' => true
308  ];
309 
313  protected string $real_filename = '';
314 
324  public function __construct(string $root = '', array $options = null)
325  {
326  global $DIC;
327  $DIC = $DIC instanceof Container ? $DIC : new Container([]);
328  $this->block_cache = $DIC->globalCache()->get(new BlockCache());
329  $this->variable_cache = $DIC->globalCache()->get(new VariableCache());
330  $this->template_cache = $DIC->globalCache()->get(new TemplateCache());
331 
332  if (!is_null($options)) {
333  $this->setOptions($options);
334  }
335  $this->variablesRegExp = '@' . $this->openingDelimiter .
336  '(' . $this->variablenameRegExp . ')' .
337  $this->closingDelimiter . '@sm';
338  $this->removeVariablesRegExp = '@' . $this->openingDelimiter .
339  "\s*(" . $this->variablenameRegExp .
340  ")\s*" . $this->closingDelimiter . '@sm';
341 
342  $this->blockRegExp = '@<!--\s+BEGIN\s+(' . $this->blocknameRegExp .
343  ')\s+-->(.*)<!--\s+END\s+\1\s+-->@sm';
344 
345  $this->setRoot($root);
346  }
347 
353  public function setOption(string $option, $value): int
354  {
355  if (array_key_exists($option, $this->_options)) {
356  $this->_options[$option] = $value;
357  return self::IT_OK;
358  }
359 
360  throw new ilTemplateException($this->errorMessage(self::IT_UNKNOWN_OPTION) . ": '$option'");
361  }
362 
368  public function setOptions(array $options): int
369  {
370  foreach ($options as $option => $value) {
371  $this->setOption($option, $value);
372  }
373 
374  return self::IT_OK;
375  }
376 
381  public function show(string $block = self::IT_DEFAULT_BLOCK): void
382  {
383  print $this->get($block);
384  }
385 
390  public function get(string $block = self::IT_DEFAULT_BLOCK): string
391  {
392  if ($block === self::IT_DEFAULT_BLOCK && !$this->flagGlobalParsed) {
393  $this->parse();
394  }
395 
396  if (!isset($this->blocklist[$block])) {
397  throw new ilTemplateException($this->errorMessage(self::IT_BLOCK_NOT_FOUND) . '"' . $block . "'");
398  }
399 
400  if (isset($this->blockdata[$block])) {
401  $ret = $this->blockdata[$block];
402  if ($this->clearCache) {
403  unset($this->blockdata[$block]);
404  }
405  if ($this->_options['preserve_data']) {
406  $ret = str_replace(
407  $this->openingDelimiter .
408  '%preserved%' . $this->closingDelimiter,
409  $this->openingDelimiter,
410  $ret
411  );
412  }
413  return $ret;
414  }
415 
416  return '';
417  }
418 
426  public function parse(string $block = self::IT_DEFAULT_BLOCK, bool $flag_recursion = false): bool
427  {
428  static $regs, $values;
429 
430  if (!isset($this->blocklist[$block])) {
431  throw new ilTemplateException($this->errorMessage(self::IT_BLOCK_NOT_FOUND) . '"' . $block . "'");
432  }
433 
434  if (self::IT_DEFAULT_BLOCK === $block) {
435  $this->flagGlobalParsed = true;
436  }
437 
438  if (!$flag_recursion) {
439  $regs = [];
440  $values = [];
441  }
442  $outer = $this->blocklist[$block];
443  $empty = true;
444 
445  if ($this->clearCacheOnParse) {
446  foreach ($this->variableCache as $name => $value) {
447  $regs[] = $this->openingDelimiter .
448  $name . $this->closingDelimiter;
449  $values[] = $value;
450  $empty = false;
451  }
452  $this->variableCache = [];
453  } else {
454  foreach ($this->blockvariables[$block] as $allowedvar => $v) {
455  if (isset($this->variableCache[$allowedvar])) {
456  $regs[] = $this->openingDelimiter .
457  $allowedvar . $this->closingDelimiter;
458  $values[] = $this->variableCache[$allowedvar];
459  unset($this->variableCache[$allowedvar]);
460  $empty = false;
461  }
462  }
463  }
464 
465  if (isset($this->blockinner[$block])) {
466  foreach ($this->blockinner[$block] as $k => $innerblock) {
467  $this->parse($innerblock, true);
468  if ($this->blockdata[$innerblock] !== '') {
469  $empty = false;
470  }
471 
472  $placeholder = $this->openingDelimiter . "__" .
473  $innerblock . "__" . $this->closingDelimiter;
474  $outer = str_replace(
475  $placeholder,
476  $this->blockdata[$innerblock],
477  $outer
478  );
479  $this->blockdata[$innerblock] = "";
480  }
481  }
482 
483  if (!$flag_recursion && 0 !== count($values)) {
484  if ($this->_options['use_preg']) {
485  $regs = array_map(
486  [
487  &$this,
488  '_addPregDelimiters'
489  ],
490  $regs
491  );
492  $funcReplace = 'preg_replace';
493  } else {
494  $funcReplace = 'str_replace';
495  }
496 
497  if ($this->_options['preserve_data']) {
498  $values = array_map(
499  [&$this, '_preserveOpeningDelimiter'],
500  $values
501  );
502  }
503 
504  $outer = $funcReplace($regs, $values, $outer);
505 
506  if ($this->removeUnknownVariables) {
507  $outer = preg_replace($this->removeVariablesRegExp, "", $outer);
508  }
509  }
510 
511  if ($empty) {
512  if (!$this->removeEmptyBlocks) {
513  $this->blockdata[$block] .= $outer;
514  } elseif (isset($this->touchedBlocks[$block])) {
515  $this->blockdata[$block] .= $outer;
516  unset($this->touchedBlocks[$block]);
517  }
518  } elseif (empty($this->blockdata[$block])) {
519  $this->blockdata[$block] = $outer;
520  } else {
521  $this->blockdata[$block] .= $outer;
522  }
523 
524  return $empty;
525  }
526 
531  public function parseCurrentBlock(): bool
532  {
533  return $this->parse($this->currentBlock);
534  }
535 
546  public function setVariable($variable, $value = ''): void
547  {
548  if (is_array($variable)) {
549  $this->variableCache = array_merge(
550  $this->variableCache,
551  $variable
552  );
553  } else {
554  $this->variableCache[$variable] = $value;
555  }
556  }
557 
563  public function setCurrentBlock(string $block = self::IT_DEFAULT_BLOCK): bool
564  {
565  if (!isset($this->blocklist[$block])) {
566  throw new ilTemplateException($this->errorMessage(self::IT_BLOCK_NOT_FOUND) . '"' . $block . "'");
567  }
568 
569  $this->currentBlock = $block;
570 
571  return true;
572  }
573 
578  public function touchBlock(string $block): bool
579  {
580  if (!isset($this->blocklist[$block])) {
581  throw new ilTemplateException($this->errorMessage(self::IT_BLOCK_NOT_FOUND) . '"' . $block . "'");
582  }
583 
584  $this->touchedBlocks[$block] = true;
585 
586  return true;
587  }
588 
596  protected function init(): void
597  {
598  $this->free();
599 
600  if (($blockdata = $this->block_cache->get($this->real_filename, new ListTransformation(new StringTransformation()))) !== null) {
601  $this->blockdata = $blockdata['blockdata'];
602  $this->blocklist = $blockdata['blocklist'];
603  } else {
604  $this->findBlocks($this->template);
605  $blockdata['blockdata'] = $this->blockdata;
606  $blockdata['blocklist'] = $this->blocklist;
607  $this->block_cache->set($this->real_filename, $blockdata);
608  }
609 
610  // we don't need it any more
611  $this->template = '';
612 
613  if (($blockvariables = $this->variable_cache->get($this->real_filename))!==null) {
614  $this->blockvariables = $blockvariables;
615  } else {
616  $this->buildBlockvariablelist();
617  $this->variable_cache->set($this->real_filename, $this->blockvariables);
618  }
619  }
620 
625  public function free(): void
626  {
627  $this->err = [];
628 
629  $this->currentBlock = self::IT_DEFAULT_BLOCK;
630 
631  $this->variableCache = [];
632  $this->blocklist = [];
633  $this->touchedBlocks = [];
634 
635  $this->flagBlocktrouble = false;
636  $this->flagGlobalParsed = false;
637  }
638 
645  public function setTemplate(
646  string $template,
647  bool $removeUnknownVariables = true,
648  bool $removeEmptyBlocks = true
649  ): bool {
650  $this->removeUnknownVariables = $removeUnknownVariables;
651  $this->removeEmptyBlocks = $removeEmptyBlocks;
652 
653  if ($template === '' && $this->flagCacheTemplatefile) {
654  $this->variableCache = [];
655  $this->blockdata = [];
656  $this->touchedBlocks = [];
657  $this->currentBlock = self::IT_DEFAULT_BLOCK;
658  } else {
659  $this->template =
660  '<!-- BEGIN ' . self::IT_DEFAULT_BLOCK . ' -->' .
661  $template .
662  '<!-- END ' . self::IT_DEFAULT_BLOCK . ' -->';
663  $this->init();
664  }
665 
666  if ($this->flagBlocktrouble) {
667  return false;
668  }
669 
670  return true;
671  }
672 
677  public function loadTemplatefile(
678  string $filename,
679  bool $removeUnknownVariables = true,
680  bool $removeEmptyBlocks = true
681  ): bool {
682  $template = '';
683  if (!$this->flagCacheTemplatefile ||
684  $this->lastTemplatefile !== $filename
685  ) {
686  $template = $this->getFile($filename);
687  }
688  $this->lastTemplatefile = $filename;
689 
690  return $template !== '' && $this->setTemplate(
691  $template,
692  $removeUnknownVariables,
693  $removeEmptyBlocks
694  );
695  }
696 
703  public function setRoot(string $root): void
704  {
705  if ($root !== '' && substr($root, -1) !== '/') {
706  $root .= '/';
707  }
708 
709  $this->fileRoot = $root;
710  }
711 
715  public function buildBlockvariablelist(): void
716  {
717  foreach ($this->blocklist as $name => $content) {
718  preg_match_all($this->variablesRegExp, $content, $regs);
719 
720  if (count($regs[1]) !== 0) {
721  foreach ($regs[1] as $var) {
722  $this->blockvariables[$name][$var] = true;
723  }
724  } else {
725  $this->blockvariables[$name] = [];
726  }
727  }
728  }
729 
734  public function findBlocks(string $string): array
735  {
736  $blocklist = [];
737  if (preg_match_all($this->blockRegExp, $string, $regs, PREG_SET_ORDER)) {
738  foreach ($regs as $match) {
739  $blockname = $match[1];
740  $blockcontent = $match[2];
741 
742  if (isset($this->blocklist[$blockname])) {
743  throw new ilTemplateException($this->errorMessage(self::IT_BLOCK_DUPLICATE, $blockname));
744  }
745 
746  $this->blocklist[$blockname] = $blockcontent;
747  $this->blockdata[$blockname] = "";
748 
749  $blocklist[] = $blockname;
750 
751  $inner = $this->findBlocks($blockcontent);
752  foreach ($inner as $name) {
753  $pattern = sprintf(
754  '@<!--\s+BEGIN\s+%s\s+-->(.*)<!--\s+END\s+%s\s+-->@sm',
755  $name,
756  $name
757  );
758 
759  $this->blocklist[$blockname] = preg_replace(
760  $pattern,
761  $this->openingDelimiter .
762  '__' . $name . '__' .
763  $this->closingDelimiter,
764  $this->blocklist[$blockname]
765  );
766  $this->blockinner[$blockname][] = $name;
767  $this->blockparents[$name] = $blockname;
768  }
769  }
770  }
771 
772  return $blocklist;
773  }
774 
779  public function getFile(string $filename): string
780  {
781  if ($filename[0] === '/' && substr($this->fileRoot, -1) === '/') {
782  $filename = substr($filename, 1);
783  }
784 
785  $filename = $this->fileRoot . $filename;
786 
787  $this->real_filename = $filename;
788 
789  if (($content = $this->template_cache->get($filename, new StringTransformation())) === null) {
790  if (!($fh = @fopen($filename, 'rb'))) {
791  throw new ilTemplateException($this->errorMessage(self::IT_TPL_NOT_FOUND) . ': "' . $filename . '"');
792  }
793 
794  $fsize = filesize($filename);
795  if ($fsize < 1) {
796  fclose($fh);
797  return '';
798  }
799 
800  $content = fread($fh, $fsize);
801  $this->template_cache->set($filename, $content);
802  fclose($fh);
803  }
804 
805  return $content;
806  }
807 
812  public function _addPregDelimiters(string $str): string
813  {
814  return '@' . $str . '@';
815  }
816 
820  public function _preserveOpeningDelimiter(string $str): string
821  {
822  return (false === strpos($str, $this->openingDelimiter)) ?
823  $str :
824  str_replace(
825  $this->openingDelimiter,
826  $this->openingDelimiter .
827  '%preserved%' . $this->closingDelimiter,
828  $str
829  );
830  }
831 
835  public function errorMessage(int $value, string $blockname = ''): string
836  {
837  static $errorMessages;
838  if (!isset($errorMessages)) {
839  $errorMessages = [
840  self::IT_OK => '',
841  self::IT_ERROR => 'unknown error',
842  self::IT_TPL_NOT_FOUND => 'Cannot read the template file',
843  self::IT_BLOCK_NOT_FOUND => 'Cannot find this block',
844  self::IT_BLOCK_DUPLICATE => 'The name of a block must be' .
845  ' uniquewithin a template.' .
846  ' Found "' . $blockname . '" twice.' .
847  'Unpredictable results ' .
848  'may appear.',
849  self::IT_UNKNOWN_OPTION => 'Unknown option'
850  ];
851  }
852 
853  return $errorMessages[$value] ?? $errorMessages[self::IT_ERROR];
854  }
855 }
string $openingDelimiter
First character of a variable placeholder ( _{_VARIABLE} ).
Definition: IT.php:146
string $closingDelimiter
Last character of a variable placeholder ( {VARIABLE_}_ ).
Definition: IT.php:151
const IT_BLOCK_DUPLICATE
Definition: IT.php:126
const IT_OK
Definition: IT.php:122
setOptions(array $options)
Sets the options for the template class.
Definition: IT.php:368
setTemplate(string $template, bool $removeUnknownVariables=true, bool $removeEmptyBlocks=true)
Sets the template.
Definition: IT.php:645
bool $removeEmptyBlocks
Controls the handling of empty blocks, default is remove.
Definition: IT.php:186
const IT_UNKNOWN_OPTION
Definition: IT.php:127
string $currentBlock
Name of the current block.
Definition: IT.php:196
array $_options
$_options[&#39;preserve_data&#39;] Whether to substitute variables and remove empty placeholders in data pass...
Definition: IT.php:305
string $template
Content of the template.
Definition: IT.php:201
array $blockparents
Array of block parents.
Definition: IT.php:221
array $blockvariables
Array of variables in a block.
Definition: IT.php:216
string $variablesRegExp
RegExp used to find variable placeholder, filled by the constructor.
Definition: IT.php:171
const IT_DEFAULT_BLOCK
Definition: IT.php:128
bool $clearCacheOnParse
Clear the variable cache on parse? If you&#39;re not an expert just leave the default false...
Definition: IT.php:263
string $removeVariablesRegExp
RegExp used to strip unused variable placeholder.
Definition: IT.php:176
bool $flagCacheTemplatefile
EXPERIMENTAL! FIXME! Flag indication that a template gets cached.
Definition: IT.php:290
string $fileRoot
Root directory for all file operations.
Definition: IT.php:269
bool $flagBlocktrouble
Internal flag indicating that a blockname was used multiple times.
Definition: IT.php:274
Customizing of pimple-DIC for ILIAS.
Definition: Container.php:35
bool $removeUnknownVariables
Controls the handling of unknown variables, default is remove.
Definition: IT.php:181
findBlocks(string $string)
Recusively builds a list of all blocks within the template.
Definition: IT.php:734
global $DIC
Definition: feed.php:28
free()
Clears all datafields of the object.
Definition: IT.php:625
string $blocknameRegExp
RegExp matching a block in the template.
Definition: IT.php:158
setOption(string $option, $value)
Sets the option for the template class.
Definition: IT.php:353
string $lastTemplatefile
EXPERIMENTAL! FIXME!
Definition: IT.php:295
setVariable($variable, $value='')
Sets a variable value.
Definition: IT.php:546
show(string $block=self::IT_DEFAULT_BLOCK)
Print a certain block with all replacements done.
Definition: IT.php:381
bool $flagGlobalParsed
Flag indicating that the global block was parsed.
Definition: IT.php:279
getFile(string $filename)
Reads a file from disk and returns its content.
Definition: IT.php:779
touchBlock(string $block)
Preserves an empty block even if removeEmptyBlocks is true.
Definition: IT.php:578
string $blockRegExp
RegExp used to find blocks an their content, filled by the constructor.
Definition: IT.php:191
_addPregDelimiters(string $str)
Adds delimiters to a string, so it can be used as a pattern in preg_* functions.
Definition: IT.php:812
errorMessage(int $value, string $blockname='')
Return a textual error message for a IT error code.
Definition: IT.php:835
Integrated Template - IT Well there&#39;s not much to say about it.
Definition: IT.php:120
array string $real_filename
Holds the real template file name.
Definition: IT.php:313
_preserveOpeningDelimiter(string $str)
Replaces an opening delimiter by a special string.
Definition: IT.php:820
const IT_TPL_NOT_FOUND
Definition: IT.php:124
bool $clearCache
Clear cache on get()?
Definition: IT.php:141
$filename
Definition: buildRTE.php:78
parseCurrentBlock()
Parses the current block.
Definition: IT.php:531
buildBlockvariablelist()
Build a list of all variables within of a block.
Definition: IT.php:715
ILIAS Cache Container Container $variable_cache
Definition: IT.php:130
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:563
const IT_BLOCK_NOT_FOUND
Definition: IT.php:125
setRoot(string $root)
Sets the file root.
Definition: IT.php:703
ILIAS Cache Container Container $block_cache
Definition: IT.php:129
const IT_ERROR
Definition: IT.php:123
string $variablenameRegExp
RegExp matching a variable placeholder in the template.
Definition: IT.php:165
array $touchedBlocks
List of blocks to preverse even if they are "empty".
Definition: IT.php:247
init()
Clears all datafields of the object and rebuild the internal blocklist LoadTemplatefile() and setTemp...
Definition: IT.php:596
loadTemplatefile(string $filename, bool $removeUnknownVariables=true, bool $removeEmptyBlocks=true)
Reads a template file from the disk.
Definition: IT.php:677
parse(string $block=self::IT_DEFAULT_BLOCK, bool $flag_recursion=false)
Parses the given block.
Definition: IT.php:426
array $blockinner
Array of inner blocks of a block.
Definition: IT.php:226
array $err
Contains the error objects.
Definition: IT.php:136
array $blockdata
Array with the parsed content of a block.
Definition: IT.php:211
array $variableCache
Variable cache.
Definition: IT.php:255
array $blocklist
Array of all blocks and their content.
Definition: IT.php:206
ILIAS Cache Container Container $template_cache
Definition: IT.php:131
__construct(string $root='', array $options=null)
Builds some complex regular expressions and optinally sets the file root directory.
Definition: IT.php:324