ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
IT.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
21// +----------------------------------------------------------------------+
22// +----------------------------------------------------------------------+
23// | This source file is subject to the New BSD license, That is bundled |
24// | the world-wide-web at |
25// | http://www.opensource.org/licenses/bsd-license.php |
26// | If you did not receive a copy of the new BSD license and are unable |
27// | to obtain it through the world-wide-web, please send a note to |
28// | pajoye@php.net, so we can mail you a copy immediately. |
29// +----------------------------------------------------------------------+
30// | Author: Ulf Wendel <ulf.wendel@phpdoc.de> |
31// | Pierre-Alain Joye <pajoye@php.net> |
32// +----------------------------------------------------------------------+
40
41require_once __DIR__ . '/../../exceptions/class.ilTemplateException.php';
42
119{
120 public const IT_OK = 1;
121 public const IT_ERROR = -1;
122 public const IT_TPL_NOT_FOUND = -2;
123 public const IT_BLOCK_NOT_FOUND = -3;
124 public const IT_BLOCK_DUPLICATE = -4;
125 public const IT_UNKNOWN_OPTION = -6;
126 public const IT_DEFAULT_BLOCK = '__global__';
127 private \ILIAS\Cache\Container\Container $block_cache;
128 private \ILIAS\Cache\Container\Container $variable_cache;
129 private \ILIAS\Cache\Container\Container $template_cache;
130
134 public array $err = [];
135
139 public bool $clearCache = false;
140
144 public string $openingDelimiter = '{';
145
149 public string $closingDelimiter = '}';
150
156 public string $blocknameRegExp = '[\.0-9A-Za-z_-]+';
157
163 public string $variablenameRegExp = '[\.0-9A-Za-z_-]+';
164
169 public string $variablesRegExp = '';
170
174 public string $removeVariablesRegExp = '';
175
179 public bool $removeUnknownVariables = true;
180
184 public bool $removeEmptyBlocks = true;
185
189 public string $blockRegExp = '';
190
195
199 public string $template = '';
200
204 public array $blocklist = [];
205
209 public array $blockdata = [];
210
214 public array $blockvariables = [];
215
219 public array $blockparents = [];
220
224 public array $blockinner = [];
225
245 public array $touchedBlocks = [];
246
253 public array $variableCache = [];
254
261 public bool $clearCacheOnParse = false;
262
267 public string $fileRoot = '';
268
272 public bool $flagBlocktrouble = false;
273
277 public bool $flagGlobalParsed = false;
278
288 public bool $flagCacheTemplatefile = true;
289
293 public string $lastTemplatefile = '';
294
303 public array $_options = [
304 'preserve_data' => false,
305 'use_preg' => true
306 ];
307
311 protected string $real_filename = '';
312
322 public function __construct(string $root = '', ?array $options = null)
323 {
324 global $DIC;
325 $DIC = $DIC instanceof Container ? $DIC : new Container([]);
326 $this->block_cache = $DIC->globalCache()->get(new BlockCache());
327 $this->variable_cache = $DIC->globalCache()->get(new VariableCache());
328 $this->template_cache = $DIC->globalCache()->get(new TemplateCache());
329
330 if (!is_null($options)) {
331 $this->setOptions($options);
332 }
333 $this->variablesRegExp = '@' . $this->openingDelimiter .
334 '(' . $this->variablenameRegExp . ')' .
335 $this->closingDelimiter . '@sm';
336 $this->removeVariablesRegExp = '@' . $this->openingDelimiter .
337 "\s*(" . $this->variablenameRegExp .
338 ")\s*" . $this->closingDelimiter . '@sm';
339
340 $this->blockRegExp = '@<!--\s+BEGIN\s+(' . $this->blocknameRegExp .
341 ')\s+-->(.*)<!--\s+END\s+\1\s+-->@sm';
342
343 $this->setRoot($root);
344 }
345
351 public function setOption(string $option, $value): int
352 {
353 if (array_key_exists($option, $this->_options)) {
354 $this->_options[$option] = $value;
355 return self::IT_OK;
356 }
357
358 throw new ilTemplateException($this->errorMessage(self::IT_UNKNOWN_OPTION) . ": '$option'");
359 }
360
366 public function setOptions(array $options): int
367 {
368 foreach ($options as $option => $value) {
369 $this->setOption($option, $value);
370 }
371
372 return self::IT_OK;
373 }
374
379 public function show(string $block = self::IT_DEFAULT_BLOCK): void
380 {
381 print $this->get($block);
382 }
383
388 public function get(string $block = self::IT_DEFAULT_BLOCK): string
389 {
390 if ($block === self::IT_DEFAULT_BLOCK && !$this->flagGlobalParsed) {
391 $this->parse();
392 }
393
394 if (!isset($this->blocklist[$block])) {
395 throw new ilTemplateException($this->errorMessage(self::IT_BLOCK_NOT_FOUND) . '"' . $block . "'");
396 }
397
398 if (isset($this->blockdata[$block])) {
399 $ret = $this->blockdata[$block];
400 if ($this->clearCache) {
401 unset($this->blockdata[$block]);
402 }
403 if ($this->_options['preserve_data']) {
404 $ret = str_replace(
405 $this->openingDelimiter .
406 '%preserved%' . $this->closingDelimiter,
407 $this->openingDelimiter,
408 $ret
409 );
410 }
411 return $ret;
412 }
413
414 return '';
415 }
416
424 public function parse(string $block = self::IT_DEFAULT_BLOCK, bool $flag_recursion = false): bool
425 {
426 static $regs, $values;
427
428 if (!isset($this->blocklist[$block])) {
429 throw new ilTemplateException($this->errorMessage(self::IT_BLOCK_NOT_FOUND) . '"' . $block . "'");
430 }
431
432 if (self::IT_DEFAULT_BLOCK === $block) {
433 $this->flagGlobalParsed = true;
434 }
435
436 if (!$flag_recursion) {
437 $regs = [];
438 $values = [];
439 }
440 $outer = $this->blocklist[$block];
441 $empty = true;
442
443 if ($this->clearCacheOnParse) {
444 foreach ($this->variableCache as $name => $value) {
445 $regs[] = $this->openingDelimiter .
447 $values[] = $value;
448 $empty = false;
449 }
450 $this->variableCache = [];
451 } else {
452 foreach ($this->blockvariables[$block] as $allowedvar => $v) {
453 if (isset($this->variableCache[$allowedvar])) {
454 $regs[] = $this->openingDelimiter .
455 $allowedvar . $this->closingDelimiter;
456 $values[] = $this->variableCache[$allowedvar];
457 unset($this->variableCache[$allowedvar]);
458 $empty = false;
459 }
460 }
461 }
462
463 if (isset($this->blockinner[$block])) {
464 foreach ($this->blockinner[$block] as $k => $innerblock) {
465 $this->parse($innerblock, true);
466 if ($this->blockdata[$innerblock] !== '') {
467 $empty = false;
468 }
469
470 $placeholder = $this->openingDelimiter . "__" .
471 $innerblock . "__" . $this->closingDelimiter;
472 $outer = str_replace(
473 $placeholder,
474 $this->blockdata[$innerblock],
475 $outer
476 );
477 $this->blockdata[$innerblock] = "";
478 }
479 }
480
481 if (!$flag_recursion && 0 !== count($values)) {
482 if ($this->_options['use_preg']) {
483 $regs = array_map(
484 [
485 &$this,
486 '_addPregDelimiters'
487 ],
488 $regs
489 );
490 $funcReplace = 'preg_replace';
491 } else {
492 $funcReplace = 'str_replace';
493 }
494
495 if ($this->_options['preserve_data']) {
496 $values = array_map(
497 [&$this, '_preserveOpeningDelimiter'],
498 $values
499 );
500 }
501
502 $outer = $funcReplace($regs, $values, $outer);
503
504 if ($this->removeUnknownVariables) {
505 $outer = preg_replace($this->removeVariablesRegExp, "", $outer);
506 }
507 }
508
509 if ($empty) {
510 if (!$this->removeEmptyBlocks) {
511 $this->blockdata[$block] .= $outer;
512 } elseif (isset($this->touchedBlocks[$block])) {
513 $this->blockdata[$block] .= $outer;
514 unset($this->touchedBlocks[$block]);
515 }
516 } elseif (empty($this->blockdata[$block])) {
517 $this->blockdata[$block] = $outer;
518 } else {
519 $this->blockdata[$block] .= $outer;
520 }
521
522 return $empty;
523 }
524
529 public function parseCurrentBlock(): bool
530 {
531 return $this->parse($this->currentBlock);
532 }
533
544 public function setVariable($variable, $value = ''): void
545 {
546 if (is_array($variable)) {
547 $this->variableCache = array_merge(
548 $this->variableCache,
549 $variable
550 );
551 } else {
552 $this->variableCache[$variable] = $value;
553 }
554 }
555
561 public function setCurrentBlock(string $block = self::IT_DEFAULT_BLOCK): bool
562 {
563 if (!isset($this->blocklist[$block])) {
564 throw new ilTemplateException($this->errorMessage(self::IT_BLOCK_NOT_FOUND) . '"' . $block . "'");
565 }
566
567 $this->currentBlock = $block;
568
569 return true;
570 }
571
576 public function touchBlock(string $block): bool
577 {
578 if (!isset($this->blocklist[$block])) {
579 throw new ilTemplateException($this->errorMessage(self::IT_BLOCK_NOT_FOUND) . '"' . $block . "'");
580 }
581
582 $this->touchedBlocks[$block] = true;
583
584 return true;
585 }
586
594 protected function init(): void
595 {
596 $this->free();
597
598 if (($blockdata = $this->block_cache->get($this->real_filename, new ListTransformation(new StringTransformation()))) !== null) {
599 $this->blockdata = $blockdata['blockdata'];
600 $this->blocklist = $blockdata['blocklist'];
601 } else {
602 $this->findBlocks($this->template);
603 $blockdata['blockdata'] = $this->blockdata;
604 $blockdata['blocklist'] = $this->blocklist;
605 $this->block_cache->set($this->real_filename, $blockdata);
606 }
607
608 // we don't need it any more
609 $this->template = '';
610
611 if (($blockvariables = $this->variable_cache->get($this->real_filename))!==null) {
612 $this->blockvariables = $blockvariables;
613 } else {
614 $this->buildBlockvariablelist();
615 $this->variable_cache->set($this->real_filename, $this->blockvariables);
616 }
617 }
618
623 public function free(): void
624 {
625 $this->err = [];
626
627 $this->currentBlock = self::IT_DEFAULT_BLOCK;
628
629 $this->variableCache = [];
630 $this->blocklist = [];
631 $this->touchedBlocks = [];
632
633 $this->flagBlocktrouble = false;
634 $this->flagGlobalParsed = false;
635 }
636
643 public function setTemplate(
644 string $template,
645 bool $removeUnknownVariables = true,
646 bool $removeEmptyBlocks = true
647 ): bool {
648 $this->removeUnknownVariables = $removeUnknownVariables;
649 $this->removeEmptyBlocks = $removeEmptyBlocks;
650
651 if ($template === '' && $this->flagCacheTemplatefile) {
652 $this->variableCache = [];
653 $this->blockdata = [];
654 $this->touchedBlocks = [];
655 $this->currentBlock = self::IT_DEFAULT_BLOCK;
656 } else {
657 $this->template =
658 '<!-- BEGIN ' . self::IT_DEFAULT_BLOCK . ' -->' .
659 $template .
660 '<!-- END ' . self::IT_DEFAULT_BLOCK . ' -->';
661 $this->init();
662 }
663
664 if ($this->flagBlocktrouble) {
665 return false;
666 }
667
668 return true;
669 }
670
675 public function loadTemplatefile(
676 string $filename,
677 bool $removeUnknownVariables = true,
678 bool $removeEmptyBlocks = true
679 ): bool {
680 $template = '';
681 if (!$this->flagCacheTemplatefile ||
682 $this->lastTemplatefile !== $filename
683 ) {
684 $template = $this->getFile($filename);
685 }
686 $this->lastTemplatefile = $filename;
687
688 return $template !== '' && $this->setTemplate(
689 $template,
690 $removeUnknownVariables,
691 $removeEmptyBlocks
692 );
693 }
694
701 public function setRoot(string $root): void
702 {
703 if ($root !== '' && substr($root, -1) !== '/') {
704 $root .= '/';
705 }
706
707 $this->fileRoot = $root;
708 }
709
713 public function buildBlockvariablelist(): void
714 {
715 foreach ($this->blocklist as $name => $content) {
716 preg_match_all($this->variablesRegExp, $content, $regs);
717
718 if (count($regs[1]) !== 0) {
719 foreach ($regs[1] as $var) {
720 $this->blockvariables[$name][$var] = true;
721 }
722 } else {
723 $this->blockvariables[$name] = [];
724 }
725 }
726 }
727
732 public function findBlocks(string $string): array
733 {
734 $blocklist = [];
735 if (preg_match_all($this->blockRegExp, $string, $regs, PREG_SET_ORDER)) {
736 foreach ($regs as $match) {
737 $blockname = $match[1];
738 $blockcontent = $match[2];
739
740 if (isset($this->blocklist[$blockname])) {
741 throw new ilTemplateException($this->errorMessage(self::IT_BLOCK_DUPLICATE, $blockname));
742 }
743
744 $this->blocklist[$blockname] = $blockcontent;
745 $this->blockdata[$blockname] = "";
746
747 $blocklist[] = $blockname;
748
749 $inner = $this->findBlocks($blockcontent);
750 foreach ($inner as $name) {
751 $pattern = sprintf(
752 '@<!--\s+BEGIN\s+%s\s+-->(.*)<!--\s+END\s+%s\s+-->@sm',
753 $name,
754 $name
755 );
756
757 $this->blocklist[$blockname] = preg_replace(
758 $pattern,
759 $this->openingDelimiter .
760 '__' . $name . '__' .
761 $this->closingDelimiter,
762 $this->blocklist[$blockname]
763 );
764 $this->blockinner[$blockname][] = $name;
765 $this->blockparents[$name] = $blockname;
766 }
767 }
768 }
769
770 return $blocklist;
771 }
772
777 public function getFile(string $filename): string
778 {
779 if ($filename[0] === '/' && substr($this->fileRoot, -1) === '/') {
780 $filename = substr($filename, 1);
781 }
782
783 $filename = $this->fileRoot . $filename;
784
785 $this->real_filename = $filename;
786
787 if (($content = $this->template_cache->get($filename, new StringTransformation())) === null) {
788 if (!($fh = @fopen($filename, 'rb'))) {
789 throw new ilTemplateException($this->errorMessage(self::IT_TPL_NOT_FOUND) . ': "' . $filename . '"');
790 }
791
792 $fsize = filesize($filename);
793 if ($fsize < 1) {
794 fclose($fh);
795 return '';
796 }
797
798 $content = fread($fh, $fsize);
799 $this->template_cache->set($filename, $content);
800 fclose($fh);
801 }
802
803 return $content;
804 }
805
810 public function _addPregDelimiters(string $str): string
811 {
812 return '@' . $str . '@';
813 }
814
818 public function _preserveOpeningDelimiter(string $str): string
819 {
820 return (false === strpos($str, $this->openingDelimiter)) ?
821 $str :
822 str_replace(
823 $this->openingDelimiter,
824 $this->openingDelimiter .
825 '%preserved%' . $this->closingDelimiter,
826 $str
827 );
828 }
829
833 public function errorMessage(int $value, string $blockname = ''): string
834 {
835 static $errorMessages;
836 if (!isset($errorMessages)) {
837 $errorMessages = [
838 self::IT_OK => '',
839 self::IT_ERROR => 'unknown error',
840 self::IT_TPL_NOT_FOUND => 'Cannot read the template file',
841 self::IT_BLOCK_NOT_FOUND => 'Cannot find this block',
842 self::IT_BLOCK_DUPLICATE => 'The name of a block must be' .
843 ' uniquewithin a template.' .
844 ' Found "' . $blockname . '" twice.' .
845 'Unpredictable results ' .
846 'may appear.',
847 self::IT_UNKNOWN_OPTION => 'Unknown option'
848 ];
849 }
850
851 return $errorMessages[$value] ?? $errorMessages[self::IT_ERROR];
852 }
853}
$filename
Definition: buildRTE.php:78
Integrated Template - IT Well there's not much to say about it.
Definition: IT.php:119
string $closingDelimiter
Last character of a variable placeholder ( {VARIABLE_}_ ).
Definition: IT.php:149
string $variablenameRegExp
RegExp matching a variable placeholder in the template.
Definition: IT.php:163
string $blockRegExp
RegExp used to find blocks an their content, filled by the constructor.
Definition: IT.php:189
const IT_BLOCK_DUPLICATE
Definition: IT.php:124
bool $flagCacheTemplatefile
EXPERIMENTAL! FIXME! Flag indication that a template gets cached.
Definition: IT.php:288
const IT_ERROR
Definition: IT.php:121
free()
Clears all datafields of the object.
Definition: IT.php:623
array $variableCache
Variable cache.
Definition: IT.php:253
buildBlockvariablelist()
Build a list of all variables within of a block.
Definition: IT.php:713
init()
Clears all datafields of the object and rebuild the internal blocklist LoadTemplatefile() and setTemp...
Definition: IT.php:594
array $blockparents
Array of block parents.
Definition: IT.php:219
bool $removeEmptyBlocks
Controls the handling of empty blocks, default is remove.
Definition: IT.php:184
touchBlock(string $block)
Preserves an empty block even if removeEmptyBlocks is true.
Definition: IT.php:576
const IT_DEFAULT_BLOCK
Definition: IT.php:126
setOption(string $option, $value)
Sets the option for the template class.
Definition: IT.php:351
array $blockinner
Array of inner blocks of a block.
Definition: IT.php:224
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:561
ILIAS Cache Container Container $block_cache
Definition: IT.php:127
_addPregDelimiters(string $str)
Adds delimiters to a string, so it can be used as a pattern in preg_* functions.
Definition: IT.php:810
string $template
Content of the template.
Definition: IT.php:199
parse(string $block=self::IT_DEFAULT_BLOCK, bool $flag_recursion=false)
Parses the given block.
Definition: IT.php:424
_preserveOpeningDelimiter(string $str)
Replaces an opening delimiter by a special string.
Definition: IT.php:818
const IT_TPL_NOT_FOUND
Definition: IT.php:122
findBlocks(string $string)
Recusively builds a list of all blocks within the template.
Definition: IT.php:732
string $fileRoot
Root directory for all file operations.
Definition: IT.php:267
loadTemplatefile(string $filename, bool $removeUnknownVariables=true, bool $removeEmptyBlocks=true)
Reads a template file from the disk.
Definition: IT.php:675
const IT_UNKNOWN_OPTION
Definition: IT.php:125
array $blockdata
Array with the parsed content of a block.
Definition: IT.php:209
string $openingDelimiter
First character of a variable placeholder ( _{_VARIABLE} ).
Definition: IT.php:144
getFile(string $filename)
Reads a file from disk and returns its content.
Definition: IT.php:777
parseCurrentBlock()
Parses the current block.
Definition: IT.php:529
string $removeVariablesRegExp
RegExp used to strip unused variable placeholder.
Definition: IT.php:174
setTemplate(string $template, bool $removeUnknownVariables=true, bool $removeEmptyBlocks=true)
Sets the template.
Definition: IT.php:643
array $blocklist
Array of all blocks and their content.
Definition: IT.php:204
array $err
Contains the error objects.
Definition: IT.php:134
setRoot(string $root)
Sets the file root.
Definition: IT.php:701
const IT_OK
Definition: IT.php:120
array $blockvariables
Array of variables in a block.
Definition: IT.php:214
show(string $block=self::IT_DEFAULT_BLOCK)
Print a certain block with all replacements done.
Definition: IT.php:379
const IT_BLOCK_NOT_FOUND
Definition: IT.php:123
setVariable($variable, $value='')
Sets a variable value.
Definition: IT.php:544
bool $flagBlocktrouble
Internal flag indicating that a blockname was used multiple times.
Definition: IT.php:272
bool $flagGlobalParsed
Flag indicating that the global block was parsed.
Definition: IT.php:277
bool $clearCacheOnParse
Clear the variable cache on parse? If you're not an expert just leave the default false.
Definition: IT.php:261
bool $clearCache
Clear cache on get()?
Definition: IT.php:139
ILIAS Cache Container Container $template_cache
Definition: IT.php:129
string $currentBlock
Name of the current block.
Definition: IT.php:194
array $_options
$_options['preserve_data'] Whether to substitute variables and remove empty placeholders in data pass...
Definition: IT.php:303
__construct(string $root='', ?array $options=null)
Builds some complex regular expressions and optinally sets the file root directory.
Definition: IT.php:322
string $variablesRegExp
RegExp used to find variable placeholder, filled by the constructor.
Definition: IT.php:169
string $blocknameRegExp
RegExp matching a block in the template.
Definition: IT.php:156
string $lastTemplatefile
EXPERIMENTAL! FIXME!
Definition: IT.php:293
setOptions(array $options)
Sets the options for the template class.
Definition: IT.php:366
string $real_filename
Holds the real template file name.
Definition: IT.php:311
array $touchedBlocks
List of blocks to preverse even if they are "empty".
Definition: IT.php:245
bool $removeUnknownVariables
Controls the handling of unknown variables, default is remove.
Definition: IT.php:179
errorMessage(int $value, string $blockname='')
Return a textual error message for a IT error code.
Definition: IT.php:833
ILIAS Cache Container Container $variable_cache
Definition: IT.php:128
Customizing of pimple-DIC for ILIAS.
Definition: Container.php:36
Transform values according to custom configuration.
return true
global $DIC
Definition: shib_login.php:26