AutorÃa | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>.namespace core\output;use stdClass;/*** This class keeps track of which HTML tags are currently open.** This makes it much easier to always generate well formed XHTML output, even* if execution terminates abruptly. Any time you output some opening HTML* without the matching closing HTML, you should push the necessary close tags* onto the stack.** @copyright 2009 Tim Hunt* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later* @since Moodle 2.0* @package core* @category output*/class xhtml_container_stack {/*** @var array Stores the list of open containers.*/protected $opencontainers = [];/*** @var array In developer debug mode, stores a stack trace of all opens and* closes, so we can output helpful error messages when there is a mismatch.*/protected $log = [];/*** @var bool Store whether we are developer debug mode. We need this in* several places including in the destructor where we may not have access to $CFG.*/protected $isdebugging;/*** Constructor*/public function __construct() {global $CFG;$this->isdebugging = $CFG->debugdeveloper;}/*** Push the close HTML for a recently opened container onto the stack.** @param string $type The type of container. This is checked when {@see pop()}* is called and must match, otherwise a developer debug warning is output.* @param string $closehtml The HTML required to close the container.*/public function push($type, $closehtml) {$container = new stdClass();$container->type = $type;$container->closehtml = $closehtml;if ($this->isdebugging) {$this->log('Open', $type);}array_push($this->opencontainers, $container);}/*** Pop the HTML for the next closing container from the stack. The $type* must match the type passed when the container was opened, otherwise a* warning will be output.** @param string $type The type of container.* @return ?string the HTML required to close the container.*/public function pop($type) {if (empty($this->opencontainers)) {debugging('<p>There are no more open containers. This suggests there is a nesting problem.</p>' .$this->output_log(), DEBUG_DEVELOPER);return;}$container = array_pop($this->opencontainers);if ($container->type != $type) {debugging('<p>The type of container to be closed (' . $container->type .') does not match the type of the next open container (' . $type .'). This suggests there is a nesting problem.</p>' .$this->output_log(), DEBUG_DEVELOPER);}if ($this->isdebugging) {$this->log('Close', $type);}return $container->closehtml;}/*** Close all but the last open container. This is useful in places like error* handling, where you want to close all the open containers (apart from <body>)* before outputting the error message.** @param bool $shouldbenone assert that the stack should be empty now - causes a* developer debug warning if it isn't.* @return string the HTML required to close any open containers inside <body>.*/public function pop_all_but_last($shouldbenone = false) {if ($shouldbenone && count($this->opencontainers) != 1) {debugging('<p>Some HTML tags were opened in the body of the page but not closed.</p>' .$this->output_log(), DEBUG_DEVELOPER);}$output = '';while (count($this->opencontainers) > 1) {$container = array_pop($this->opencontainers);$output .= $container->closehtml;}return $output;}/*** You can call this function if you want to throw away an instance of this* class without properly emptying the stack (for example, in a unit test).* Calling this method stops the destruct method from outputting a developer* debug warning. After calling this method, the instance can no longer be used.*/public function discard() {$this->opencontainers = null;}/*** Adds an entry to the log.** @param string $action The name of the action* @param string $type The type of action*/protected function log($action, $type) {$this->log[] = '<li>' . $action . ' ' . $type . ' at:' .format_backtrace(debug_backtrace()) . '</li>';}/*** Outputs the log's contents as a HTML list.** @return string HTML list of the log*/protected function output_log() {return '<ul>' . implode("\n", $this->log) . '</ul>';}}// Alias this class to the old name.// This file will be autoloaded by the legacyclasses autoload system.// In future all uses of this class will be corrected and the legacy references will be removed.class_alias(xhtml_container_stack::class, \xhtml_container_stack::class);