Merge branch 'MDL-67377-master' of git://github.com/ferranrecio/moodle
[moodle.git] / lib / mustache / src / Mustache / Engine.php
CommitLineData
9bdcf579
DW
1<?php
2
3/*
4 * This file is part of Mustache.php.
5 *
703baed2 6 * (c) 2010-2017 Justin Hileman
9bdcf579
DW
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * A Mustache implementation in PHP.
14 *
15 * {@link http://defunkt.github.com/mustache}
16 *
17 * Mustache is a framework-agnostic logic-less templating language. It enforces separation of view
18 * logic from template files. In fact, it is not even possible to embed logic in the template.
19 *
20 * This is very, very rad.
21 *
22 * @author Justin Hileman {@link http://justinhileman.com}
23 */
24class Mustache_Engine
25{
d0098010 26 const VERSION = '2.13.0';
9bdcf579
DW
27 const SPEC_VERSION = '1.1.2';
28
85fa6a93
DW
29 const PRAGMA_FILTERS = 'FILTERS';
30 const PRAGMA_BLOCKS = 'BLOCKS';
31 const PRAGMA_ANCHORED_DOT = 'ANCHORED-DOT';
9bdcf579
DW
32
33 // Known pragmas
34 private static $knownPragmas = array(
85fa6a93
DW
35 self::PRAGMA_FILTERS => true,
36 self::PRAGMA_BLOCKS => true,
37 self::PRAGMA_ANCHORED_DOT => true,
9bdcf579
DW
38 );
39
40 // Template cache
41 private $templates = array();
42
43 // Environment
44 private $templateClassPrefix = '__Mustache_';
45 private $cache;
46 private $lambdaCache;
47 private $cacheLambdaTemplates = false;
48 private $loader;
49 private $partialsLoader;
50 private $helpers;
51 private $escape;
52 private $entityFlags = ENT_COMPAT;
53 private $charset = 'UTF-8';
54 private $logger;
55 private $strictCallables = false;
56 private $pragmas = array();
703baed2 57 private $delimiters;
9bdcf579
DW
58
59 // Services
60 private $tokenizer;
61 private $parser;
62 private $compiler;
63
64 /**
65 * Mustache class constructor.
66 *
67 * Passing an $options array allows overriding certain Mustache options during instantiation:
68 *
69 * $options = array(
70 * // The class prefix for compiled templates. Defaults to '__Mustache_'.
71 * 'template_class_prefix' => '__MyTemplates_',
72 *
73 * // A Mustache cache instance or a cache directory string for compiled templates.
74 * // Mustache will not cache templates unless this is set.
75 * 'cache' => dirname(__FILE__).'/tmp/cache/mustache',
76 *
77 * // Override default permissions for cache files. Defaults to using the system-defined umask. It is
78 * // *strongly* recommended that you configure your umask properly rather than overriding permissions here.
79 * 'cache_file_mode' => 0666,
80 *
81 * // Optionally, enable caching for lambda section templates. This is generally not recommended, as lambda
82 * // sections are often too dynamic to benefit from caching.
83 * 'cache_lambda_templates' => true,
84 *
703baed2
AG
85 * // Customize the tag delimiters used by this engine instance. Note that overriding here changes the
86 * // delimiters used to parse all templates and partials loaded by this instance. To override just for a
87 * // single template, use an inline "change delimiters" tag at the start of the template file:
88 * //
89 * // {{=<% %>=}}
90 * //
91 * 'delimiters' => '<% %>',
92 *
9bdcf579
DW
93 * // A Mustache template loader instance. Uses a StringLoader if not specified.
94 * 'loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'),
95 *
96 * // A Mustache loader instance for partials.
97 * 'partials_loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views/partials'),
98 *
99 * // An array of Mustache partials. Useful for quick-and-dirty string template loading, but not as
100 * // efficient or lazy as a Filesystem (or database) loader.
101 * 'partials' => array('foo' => file_get_contents(dirname(__FILE__).'/views/partials/foo.mustache')),
102 *
103 * // An array of 'helpers'. Helpers can be global variables or objects, closures (e.g. for higher order
104 * // sections), or any other valid Mustache context value. They will be prepended to the context stack,
105 * // so they will be available in any template loaded by this Mustache instance.
106 * 'helpers' => array('i18n' => function ($text) {
107 * // do something translatey here...
108 * }),
109 *
110 * // An 'escape' callback, responsible for escaping double-mustache variables.
111 * 'escape' => function ($value) {
112 * return htmlspecialchars($buffer, ENT_COMPAT, 'UTF-8');
113 * },
114 *
115 * // Type argument for `htmlspecialchars`. Defaults to ENT_COMPAT. You may prefer ENT_QUOTES.
116 * 'entity_flags' => ENT_QUOTES,
117 *
118 * // Character set for `htmlspecialchars`. Defaults to 'UTF-8'. Use 'UTF-8'.
119 * 'charset' => 'ISO-8859-1',
120 *
121 * // A Mustache Logger instance. No logging will occur unless this is set. Using a PSR-3 compatible
122 * // logging library -- such as Monolog -- is highly recommended. A simple stream logger implementation is
123 * // available as well:
124 * 'logger' => new Mustache_Logger_StreamLogger('php://stderr'),
125 *
126 * // Only treat Closure instances and invokable classes as callable. If true, values like
127 * // `array('ClassName', 'methodName')` and `array($classInstance, 'methodName')`, which are traditionally
128 * // "callable" in PHP, are not called to resolve variables for interpolation or section contexts. This
129 * // helps protect against arbitrary code execution when user input is passed directly into the template.
130 * // This currently defaults to false, but will default to true in v3.0.
131 * 'strict_callables' => true,
132 *
133 * // Enable pragmas across all templates, regardless of the presence of pragma tags in the individual
134 * // templates.
135 * 'pragmas' => [Mustache_Engine::PRAGMA_FILTERS],
136 * );
137 *
20df227e 138 * @throws Mustache_Exception_InvalidArgumentException If `escape` option is not callable
9bdcf579
DW
139 *
140 * @param array $options (default: array())
141 */
142 public function __construct(array $options = array())
143 {
144 if (isset($options['template_class_prefix'])) {
703baed2
AG
145 if ((string) $options['template_class_prefix'] === '') {
146 throw new Mustache_Exception_InvalidArgumentException('Mustache Constructor "template_class_prefix" must not be empty');
147 }
148
9bdcf579
DW
149 $this->templateClassPrefix = $options['template_class_prefix'];
150 }
151
152 if (isset($options['cache'])) {
153 $cache = $options['cache'];
154
155 if (is_string($cache)) {
156 $mode = isset($options['cache_file_mode']) ? $options['cache_file_mode'] : null;
157 $cache = new Mustache_Cache_FilesystemCache($cache, $mode);
158 }
159
160 $this->setCache($cache);
161 }
162
163 if (isset($options['cache_lambda_templates'])) {
164 $this->cacheLambdaTemplates = (bool) $options['cache_lambda_templates'];
165 }
166
167 if (isset($options['loader'])) {
168 $this->setLoader($options['loader']);
169 }
170
171 if (isset($options['partials_loader'])) {
172 $this->setPartialsLoader($options['partials_loader']);
173 }
174
175 if (isset($options['partials'])) {
176 $this->setPartials($options['partials']);
177 }
178
179 if (isset($options['helpers'])) {
180 $this->setHelpers($options['helpers']);
181 }
182
183 if (isset($options['escape'])) {
184 if (!is_callable($options['escape'])) {
185 throw new Mustache_Exception_InvalidArgumentException('Mustache Constructor "escape" option must be callable');
186 }
187
188 $this->escape = $options['escape'];
189 }
190
191 if (isset($options['entity_flags'])) {
85fa6a93 192 $this->entityFlags = $options['entity_flags'];
9bdcf579
DW
193 }
194
195 if (isset($options['charset'])) {
196 $this->charset = $options['charset'];
197 }
198
199 if (isset($options['logger'])) {
200 $this->setLogger($options['logger']);
201 }
202
203 if (isset($options['strict_callables'])) {
204 $this->strictCallables = $options['strict_callables'];
205 }
206
703baed2
AG
207 if (isset($options['delimiters'])) {
208 $this->delimiters = $options['delimiters'];
209 }
210
9bdcf579
DW
211 if (isset($options['pragmas'])) {
212 foreach ($options['pragmas'] as $pragma) {
213 if (!isset(self::$knownPragmas[$pragma])) {
214 throw new Mustache_Exception_InvalidArgumentException(sprintf('Unknown pragma: "%s".', $pragma));
215 }
216 $this->pragmas[$pragma] = true;
217 }
218 }
219 }
220
221 /**
222 * Shortcut 'render' invocation.
223 *
224 * Equivalent to calling `$mustache->loadTemplate($template)->render($context);`
225 *
226 * @see Mustache_Engine::loadTemplate
227 * @see Mustache_Template::render
228 *
229 * @param string $template
230 * @param mixed $context (default: array())
231 *
232 * @return string Rendered template
233 */
234 public function render($template, $context = array())
235 {
236 return $this->loadTemplate($template)->render($context);
237 }
238
239 /**
240 * Get the current Mustache escape callback.
241 *
242 * @return callable|null
243 */
244 public function getEscape()
245 {
246 return $this->escape;
247 }
248
249 /**
250 * Get the current Mustache entitity type to escape.
251 *
252 * @return int
253 */
254 public function getEntityFlags()
255 {
256 return $this->entityFlags;
257 }
258
259 /**
260 * Get the current Mustache character set.
261 *
262 * @return string
263 */
264 public function getCharset()
265 {
266 return $this->charset;
267 }
268
269 /**
270 * Get the current globally enabled pragmas.
271 *
272 * @return array
273 */
274 public function getPragmas()
275 {
276 return array_keys($this->pragmas);
277 }
278
279 /**
280 * Set the Mustache template Loader instance.
281 *
282 * @param Mustache_Loader $loader
283 */
284 public function setLoader(Mustache_Loader $loader)
285 {
286 $this->loader = $loader;
287 }
288
289 /**
290 * Get the current Mustache template Loader instance.
291 *
292 * If no Loader instance has been explicitly specified, this method will instantiate and return
293 * a StringLoader instance.
294 *
295 * @return Mustache_Loader
296 */
297 public function getLoader()
298 {
299 if (!isset($this->loader)) {
300 $this->loader = new Mustache_Loader_StringLoader();
301 }
302
303 return $this->loader;
304 }
305
306 /**
307 * Set the Mustache partials Loader instance.
308 *
309 * @param Mustache_Loader $partialsLoader
310 */
311 public function setPartialsLoader(Mustache_Loader $partialsLoader)
312 {
313 $this->partialsLoader = $partialsLoader;
314 }
315
316 /**
317 * Get the current Mustache partials Loader instance.
318 *
319 * If no Loader instance has been explicitly specified, this method will instantiate and return
320 * an ArrayLoader instance.
321 *
322 * @return Mustache_Loader
323 */
324 public function getPartialsLoader()
325 {
326 if (!isset($this->partialsLoader)) {
327 $this->partialsLoader = new Mustache_Loader_ArrayLoader();
328 }
329
330 return $this->partialsLoader;
331 }
332
333 /**
334 * Set partials for the current partials Loader instance.
335 *
336 * @throws Mustache_Exception_RuntimeException If the current Loader instance is immutable
337 *
338 * @param array $partials (default: array())
339 */
340 public function setPartials(array $partials = array())
341 {
342 if (!isset($this->partialsLoader)) {
343 $this->partialsLoader = new Mustache_Loader_ArrayLoader();
344 }
345
346 if (!$this->partialsLoader instanceof Mustache_Loader_MutableLoader) {
347 throw new Mustache_Exception_RuntimeException('Unable to set partials on an immutable Mustache Loader instance');
348 }
349
350 $this->partialsLoader->setTemplates($partials);
351 }
352
353 /**
354 * Set an array of Mustache helpers.
355 *
356 * An array of 'helpers'. Helpers can be global variables or objects, closures (e.g. for higher order sections), or
357 * any other valid Mustache context value. They will be prepended to the context stack, so they will be available in
358 * any template loaded by this Mustache instance.
359 *
360 * @throws Mustache_Exception_InvalidArgumentException if $helpers is not an array or Traversable
361 *
362 * @param array|Traversable $helpers
363 */
364 public function setHelpers($helpers)
365 {
366 if (!is_array($helpers) && !$helpers instanceof Traversable) {
367 throw new Mustache_Exception_InvalidArgumentException('setHelpers expects an array of helpers');
368 }
369
370 $this->getHelpers()->clear();
371
372 foreach ($helpers as $name => $helper) {
373 $this->addHelper($name, $helper);
374 }
375 }
376
377 /**
378 * Get the current set of Mustache helpers.
379 *
380 * @see Mustache_Engine::setHelpers
381 *
382 * @return Mustache_HelperCollection
383 */
384 public function getHelpers()
385 {
386 if (!isset($this->helpers)) {
387 $this->helpers = new Mustache_HelperCollection();
388 }
389
390 return $this->helpers;
391 }
392
393 /**
394 * Add a new Mustache helper.
395 *
396 * @see Mustache_Engine::setHelpers
397 *
398 * @param string $name
399 * @param mixed $helper
400 */
401 public function addHelper($name, $helper)
402 {
403 $this->getHelpers()->add($name, $helper);
404 }
405
406 /**
407 * Get a Mustache helper by name.
408 *
409 * @see Mustache_Engine::setHelpers
410 *
411 * @param string $name
412 *
413 * @return mixed Helper
414 */
415 public function getHelper($name)
416 {
417 return $this->getHelpers()->get($name);
418 }
419
420 /**
421 * Check whether this Mustache instance has a helper.
422 *
423 * @see Mustache_Engine::setHelpers
424 *
425 * @param string $name
426 *
85fa6a93 427 * @return bool True if the helper is present
9bdcf579
DW
428 */
429 public function hasHelper($name)
430 {
431 return $this->getHelpers()->has($name);
432 }
433
434 /**
435 * Remove a helper by name.
436 *
437 * @see Mustache_Engine::setHelpers
438 *
439 * @param string $name
440 */
441 public function removeHelper($name)
442 {
443 $this->getHelpers()->remove($name);
444 }
445
446 /**
447 * Set the Mustache Logger instance.
448 *
20df227e 449 * @throws Mustache_Exception_InvalidArgumentException If logger is not an instance of Mustache_Logger or Psr\Log\LoggerInterface
9bdcf579
DW
450 *
451 * @param Mustache_Logger|Psr\Log\LoggerInterface $logger
452 */
453 public function setLogger($logger = null)
454 {
455 if ($logger !== null && !($logger instanceof Mustache_Logger || is_a($logger, 'Psr\\Log\\LoggerInterface'))) {
456 throw new Mustache_Exception_InvalidArgumentException('Expected an instance of Mustache_Logger or Psr\\Log\\LoggerInterface.');
457 }
458
459 if ($this->getCache()->getLogger() === null) {
460 $this->getCache()->setLogger($logger);
461 }
462
463 $this->logger = $logger;
464 }
465
466 /**
467 * Get the current Mustache Logger instance.
468 *
469 * @return Mustache_Logger|Psr\Log\LoggerInterface
470 */
471 public function getLogger()
472 {
473 return $this->logger;
474 }
475
476 /**
477 * Set the Mustache Tokenizer instance.
478 *
479 * @param Mustache_Tokenizer $tokenizer
480 */
481 public function setTokenizer(Mustache_Tokenizer $tokenizer)
482 {
483 $this->tokenizer = $tokenizer;
484 }
485
486 /**
487 * Get the current Mustache Tokenizer instance.
488 *
489 * If no Tokenizer instance has been explicitly specified, this method will instantiate and return a new one.
490 *
491 * @return Mustache_Tokenizer
492 */
493 public function getTokenizer()
494 {
495 if (!isset($this->tokenizer)) {
496 $this->tokenizer = new Mustache_Tokenizer();
497 }
498
499 return $this->tokenizer;
500 }
501
502 /**
503 * Set the Mustache Parser instance.
504 *
505 * @param Mustache_Parser $parser
506 */
507 public function setParser(Mustache_Parser $parser)
508 {
509 $this->parser = $parser;
510 }
511
512 /**
513 * Get the current Mustache Parser instance.
514 *
515 * If no Parser instance has been explicitly specified, this method will instantiate and return a new one.
516 *
517 * @return Mustache_Parser
518 */
519 public function getParser()
520 {
521 if (!isset($this->parser)) {
522 $this->parser = new Mustache_Parser();
523 }
524
525 return $this->parser;
526 }
527
528 /**
529 * Set the Mustache Compiler instance.
530 *
531 * @param Mustache_Compiler $compiler
532 */
533 public function setCompiler(Mustache_Compiler $compiler)
534 {
535 $this->compiler = $compiler;
536 }
537
538 /**
539 * Get the current Mustache Compiler instance.
540 *
541 * If no Compiler instance has been explicitly specified, this method will instantiate and return a new one.
542 *
543 * @return Mustache_Compiler
544 */
545 public function getCompiler()
546 {
547 if (!isset($this->compiler)) {
548 $this->compiler = new Mustache_Compiler();
549 }
550
551 return $this->compiler;
552 }
553
554 /**
555 * Set the Mustache Cache instance.
556 *
557 * @param Mustache_Cache $cache
558 */
559 public function setCache(Mustache_Cache $cache)
560 {
561 if (isset($this->logger) && $cache->getLogger() === null) {
562 $cache->setLogger($this->getLogger());
563 }
564
565 $this->cache = $cache;
566 }
567
568 /**
569 * Get the current Mustache Cache instance.
570 *
571 * If no Cache instance has been explicitly specified, this method will instantiate and return a new one.
572 *
573 * @return Mustache_Cache
574 */
575 public function getCache()
576 {
577 if (!isset($this->cache)) {
578 $this->setCache(new Mustache_Cache_NoopCache());
579 }
580
581 return $this->cache;
582 }
583
584 /**
585 * Get the current Lambda Cache instance.
586 *
587 * If 'cache_lambda_templates' is enabled, this is the default cache instance. Otherwise, it is a NoopCache.
588 *
589 * @see Mustache_Engine::getCache
590 *
591 * @return Mustache_Cache
592 */
593 protected function getLambdaCache()
594 {
595 if ($this->cacheLambdaTemplates) {
596 return $this->getCache();
597 }
598
599 if (!isset($this->lambdaCache)) {
600 $this->lambdaCache = new Mustache_Cache_NoopCache();
601 }
602
603 return $this->lambdaCache;
604 }
605
606 /**
607 * Helper method to generate a Mustache template class.
608 *
703baed2
AG
609 * This method must be updated any time options are added which make it so
610 * the same template could be parsed and compiled multiple different ways.
611 *
612 * @param string|Mustache_Source $source
9bdcf579
DW
613 *
614 * @return string Mustache Template class name
615 */
616 public function getTemplateClassName($source)
617 {
703baed2
AG
618 // For the most part, adding a new option here should do the trick.
619 //
620 // Pick a value here which is unique for each possible way the template
621 // could be compiled... but not necessarily unique per option value. See
622 // escape below, which only needs to differentiate between 'custom' and
623 // 'default' escapes.
624 //
625 // Keep this list in alphabetical order :)
626 $chunks = array(
627 'charset' => $this->charset,
628 'delimiters' => $this->delimiters ? $this->delimiters : '{{ }}',
629 'entityFlags' => $this->entityFlags,
630 'escape' => isset($this->escape) ? 'custom' : 'default',
631 'key' => ($source instanceof Mustache_Source) ? $source->getKey() : 'source',
632 'pragmas' => $this->getPragmas(),
633 'strictCallables' => $this->strictCallables,
634 'version' => self::VERSION,
635 );
636
637 $key = json_encode($chunks);
638
639 // Template Source instances have already provided their own source key. For strings, just include the whole
640 // source string in the md5 hash.
641 if (!$source instanceof Mustache_Source) {
642 $key .= "\n" . $source;
643 }
644
645 return $this->templateClassPrefix . md5($key);
9bdcf579
DW
646 }
647
648 /**
649 * Load a Mustache Template by name.
650 *
651 * @param string $name
652 *
653 * @return Mustache_Template
654 */
655 public function loadTemplate($name)
656 {
657 return $this->loadSource($this->getLoader()->load($name));
658 }
659
660 /**
661 * Load a Mustache partial Template by name.
662 *
663 * This is a helper method used internally by Template instances for loading partial templates. You can most likely
664 * ignore it completely.
665 *
666 * @param string $name
667 *
668 * @return Mustache_Template
669 */
670 public function loadPartial($name)
671 {
672 try {
673 if (isset($this->partialsLoader)) {
674 $loader = $this->partialsLoader;
675 } elseif (isset($this->loader) && !$this->loader instanceof Mustache_Loader_StringLoader) {
676 $loader = $this->loader;
677 } else {
678 throw new Mustache_Exception_UnknownTemplateException($name);
679 }
680
681 return $this->loadSource($loader->load($name));
682 } catch (Mustache_Exception_UnknownTemplateException $e) {
683 // If the named partial cannot be found, log then return null.
684 $this->log(
685 Mustache_Logger::WARNING,
686 'Partial not found: "{name}"',
687 array('name' => $e->getTemplateName())
688 );
689 }
690 }
691
692 /**
693 * Load a Mustache lambda Template by source.
694 *
695 * This is a helper method used by Template instances to generate subtemplates for Lambda sections. You can most
696 * likely ignore it completely.
697 *
698 * @param string $source
699 * @param string $delims (default: null)
700 *
701 * @return Mustache_Template
702 */
703 public function loadLambda($source, $delims = null)
704 {
705 if ($delims !== null) {
706 $source = $delims . "\n" . $source;
707 }
708
709 return $this->loadSource($source, $this->getLambdaCache());
710 }
711
712 /**
713 * Instantiate and return a Mustache Template instance by source.
714 *
715 * Optionally provide a Mustache_Cache instance. This is used internally by Mustache_Engine::loadLambda to respect
716 * the 'cache_lambda_templates' configuration option.
717 *
718 * @see Mustache_Engine::loadTemplate
719 * @see Mustache_Engine::loadPartial
720 * @see Mustache_Engine::loadLambda
721 *
703baed2
AG
722 * @param string|Mustache_Source $source
723 * @param Mustache_Cache $cache (default: null)
9bdcf579
DW
724 *
725 * @return Mustache_Template
726 */
727 private function loadSource($source, Mustache_Cache $cache = null)
728 {
729 $className = $this->getTemplateClassName($source);
730
731 if (!isset($this->templates[$className])) {
732 if ($cache === null) {
733 $cache = $this->getCache();
734 }
735
736 if (!class_exists($className, false)) {
737 if (!$cache->load($className)) {
738 $compiled = $this->compile($source);
739 $cache->cache($className, $compiled);
740 }
741 }
742
743 $this->log(
744 Mustache_Logger::DEBUG,
745 'Instantiating template: "{className}"',
746 array('className' => $className)
747 );
748
749 $this->templates[$className] = new $className($this);
750 }
751
752 return $this->templates[$className];
753 }
754
755 /**
756 * Helper method to tokenize a Mustache template.
757 *
758 * @see Mustache_Tokenizer::scan
759 *
760 * @param string $source
761 *
762 * @return array Tokens
763 */
764 private function tokenize($source)
765 {
703baed2 766 return $this->getTokenizer()->scan($source, $this->delimiters);
9bdcf579
DW
767 }
768
769 /**
770 * Helper method to parse a Mustache template.
771 *
772 * @see Mustache_Parser::parse
773 *
774 * @param string $source
775 *
776 * @return array Token tree
777 */
778 private function parse($source)
779 {
780 $parser = $this->getParser();
781 $parser->setPragmas($this->getPragmas());
782
783 return $parser->parse($this->tokenize($source));
784 }
785
786 /**
787 * Helper method to compile a Mustache template.
788 *
789 * @see Mustache_Compiler::compile
790 *
703baed2 791 * @param string|Mustache_Source $source
9bdcf579
DW
792 *
793 * @return string generated Mustache template class code
794 */
795 private function compile($source)
796 {
9bdcf579
DW
797 $name = $this->getTemplateClassName($source);
798
799 $this->log(
800 Mustache_Logger::INFO,
801 'Compiling template to "{className}" class',
802 array('className' => $name)
803 );
804
703baed2
AG
805 if ($source instanceof Mustache_Source) {
806 $source = $source->getSource();
807 }
808 $tree = $this->parse($source);
809
9bdcf579
DW
810 $compiler = $this->getCompiler();
811 $compiler->setPragmas($this->getPragmas());
812
813 return $compiler->compile($source, $tree, $name, isset($this->escape), $this->charset, $this->strictCallables, $this->entityFlags);
814 }
815
816 /**
817 * Add a log record if logging is enabled.
818 *
85fa6a93
DW
819 * @param int $level The logging level
820 * @param string $message The log message
821 * @param array $context The log context
9bdcf579
DW
822 */
823 private function log($level, $message, array $context = array())
824 {
825 if (isset($this->logger)) {
826 $this->logger->log($level, $message, $context);
827 }
828 }
829}