Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.
   1  <?php
   2  
   3  /**
   4   * Implementation of Common Cartridge library based on
   5   * {@link http://www.imsglobal.org/cc/ IMS Common Cartridge Standard v1.2}
   6   *
   7   * @author Darko Miletic
   8   * @author Daniel Muhlrad (daniel.muhlrad@uvcms.com)
   9   * @version 1.0
  10   * @copyright 2009 {@link http://www.uvcms.com UVCMS e-learning}
  11   * @package cc_library
  12   *
  13   */
  14  
  15  require_once ('gral_lib/cssparser.php');
  16  
  17  /**
  18   * Base XML class
  19   *
  20   */
  21  class XMLGenericDocument {
  22      private $charset;
  23      /**
  24       * Document
  25       * @var DOMDocument
  26       */
  27      public $doc = null;
  28      /**
  29       *
  30       * Xpath
  31       * @var DOMXPath
  32       */
  33      protected $dxpath = null;
  34      protected $filename;
  35      private $filepath;
  36      private $isloaded = false;
  37      private $arrayPrefixNS = array();
  38      private $is_html = false;
  39  
  40      /**
  41       * @param string $value
  42       * @return string
  43       */
  44      public static function safexml($value) {
  45          $result = htmlspecialchars(html_entity_decode($value, ENT_QUOTES, 'UTF-8'),
  46                                     ENT_NOQUOTES,
  47                                     'UTF-8',
  48                                     false);
  49          return $result;
  50      }
  51  
  52      function __construct($ch = 'UTF-8', $validatenow = true) {
  53          $this->charset = $ch;
  54          $this->documentInit();
  55          $this->doc->validateOnParse = $validatenow;
  56      }
  57  
  58      function __destruct() {
  59          $this->dxpath = null;
  60          $this->doc    = null;
  61      }
  62  
  63      private function documentInit($withonCreate = true) {
  64          $hg = false;
  65          if ($this->isloaded) {
  66              $guardstate = $this->doc->validateOnParse;
  67              $hg = true;
  68              unset($this->dxpath);
  69              unset($this->doc);
  70              $this->isloaded = false;
  71            }
  72          $this->doc = new DOMDocument("1.0", $this->charset);
  73          $this->doc->strictErrorChecking = true;
  74          if ($hg) {
  75              $this->doc->validateOnParse = $guardstate;
  76          }
  77          $this->doc->formatOutput = true;
  78          $this->doc->preserveWhiteSpace = true;
  79          if ($withonCreate) {
  80              $this->on_create();
  81          }
  82      }
  83  
  84      public function viewXML() {
  85          return $this->doc->saveXML();
  86      }
  87  
  88      public function registerNS($prefix, $nsuri) {
  89          $this->arrayPrefixNS[$prefix] = $nsuri;
  90      }
  91  
  92      public function load($fname) {
  93          // Sine xml will remain loaded should the repeated load fail we should recreate document to be empty.
  94          $this->documentInit(false);
  95          $this->isloaded = $this->doc->load($fname);
  96          if ($this->isloaded) {
  97              $this->filename = $fname;
  98              $this->processPath();
  99              $this->is_html = false;
 100          }
 101          return $this->on_load();
 102      }
 103  
 104      public function loadUrl($url) {
 105          $this->documentInit();
 106          $this->isloaded = true;
 107          $this->doc->loadXML( file_get_contents($url) );
 108          $this->is_html = false;
 109          return $this->on_load();
 110      }
 111  
 112      public function loadHTML($content) {
 113          $this->documentInit();
 114          $this->doc->validateOnParse = false;
 115          $this->isloaded = true;
 116          $this->doc->loadHTML($content);
 117          $this->is_html = true;
 118          return $this->on_load();
 119      }
 120  
 121      public function loadXML($content) {
 122          $this->documentInit();
 123          $this->doc->validateOnParse = false;
 124          $this->isloaded = true;
 125          $this->doc->load($content);
 126          $this->is_html = true;
 127          return $this->on_load();
 128      }
 129  
 130      public function loadHTMLFile($fname) {
 131          // Sine xml will remain loaded should the repeated load fail
 132          // we should recreate document to be empty.
 133          $this->documentInit();
 134          $this->doc->validateOnParse = false;
 135          $this->isloaded = $this->doc->loadHTMLFile($fname);
 136          if ($this->isloaded) {
 137              $this->filename = $fname;
 138              $this->processPath();
 139              $this->is_html=true;
 140          }
 141          return $this->on_load();
 142      }
 143  
 144      public function loadXMLFile($fname) {
 145          // Sine xml will remain loaded should the repeated load fail
 146          // we should recreate document to be empty.
 147          $this->documentInit();
 148          $this->doc->validateOnParse = false;
 149          $this->isloaded = $this->doc->load($fname);
 150          if ($this->isloaded) {
 151              $this->filename = $fname;
 152              $this->processPath();
 153              $this->is_html = true;
 154          }
 155          return $this->on_load();
 156      }
 157  
 158  
 159      public function loadString($content) {
 160  
 161          $this->doc = new DOMDocument("1.0", $this->charset);
 162          $content = '<virtualtag>'.$content.'</virtualtag>';
 163          $this->doc->loadXML($content);
 164  
 165          return true;
 166      }
 167  
 168      public function save() {
 169          $this->saveTo($this->filename);
 170      }
 171  
 172      public function saveTo($fname) {
 173          $status = false;
 174          if ($this->on_save()) {
 175              if ($this->is_html) {
 176                  $this->doc->saveHTMLFile($fname);
 177              } else {
 178                  $this->doc->save($fname);
 179              }
 180              $this->filename = $fname;
 181              $this->processPath();
 182              $status = true;
 183          }
 184          return $status;
 185      }
 186  
 187      public function validate() {
 188          return $this->doc->validate();
 189      }
 190  
 191      public function attributeValue($path, $attrname, $node = null) {
 192          $this->chkxpath();
 193          $result = null;
 194          $resultlist = null;
 195          if (is_null($node)) {
 196              $resultlist = $this->dxpath->query($path);
 197          } else {
 198              $resultlist = $this->dxpath->query($path, $node);
 199          }
 200          if (is_object($resultlist) && ($resultlist->length > 0) && $resultlist->item(0)->hasAttribute($attrname)) {
 201              $result = $resultlist->item(0)->getAttribute($attrname);
 202          }
 203          return $result;
 204      }
 205  
 206      /**
 207       *
 208       * Get's text value of the node based on xpath query
 209       * @param string $path
 210       * @param DOMNode $node
 211       * @param int $count
 212       * @return string
 213       */
 214      public function nodeValue($path, $node = null, $count = 1) {
 215          $nd = $this->node($path, $node, $count);
 216          return $this->nodeTextValue($nd);
 217      }
 218  
 219      /**
 220       *
 221       * Get's text value of the node
 222       * @param DOMNode $node
 223       * @return string
 224       */
 225      public function nodeTextValue($node) {
 226          $result = '';
 227          if (is_object($node)) {
 228              if ($node->hasChildNodes()) {
 229                  $chnodesList = $node->childNodes;
 230                  $types = array(XML_TEXT_NODE, XML_CDATA_SECTION_NODE);
 231                  foreach ($chnodesList as $chnode) {
 232                      if (in_array($chnode->nodeType, $types)) {
 233                          $result .= $chnode->wholeText;
 234                      }
 235                  }
 236              }
 237          }
 238          return $result;
 239      }
 240  
 241      /**
 242       *
 243       * Enter description here ...
 244       * @param string $path
 245       * @param DOMNode $nd
 246       * @param int $count
 247       * @return DOMNode
 248       */
 249      public function node($path, $nd = null, $count = 1) {
 250          $result = null;
 251          $resultlist = $this->nodeList($path,$nd);
 252          if (is_object($resultlist) && ($resultlist->length > 0)) {
 253              $result = $resultlist->item($count - 1);
 254          }
 255          return $result;
 256      }
 257  
 258      /**
 259       *
 260       * Enter description here ...
 261       * @param string $path
 262       * @param DOMNode $node
 263       * @return DOMNodeList
 264       */
 265      public function nodeList($path, $node = null) {
 266  
 267          $this->chkxpath();
 268  
 269          $resultlist = null;
 270          if (is_null($node)) {
 271              $resultlist = $this->dxpath->query($path);
 272          } else {
 273              $resultlist = $this->dxpath->query($path, $node);
 274          }
 275          return $resultlist;
 276      }
 277  
 278      /**
 279       *
 280       * Create new attribute
 281       * @param string $namespace
 282       * @param string $name
 283       * @param string $value
 284       * @return DOMAttr
 285       */
 286      public function create_attribute_ns($namespace, $name, $value = null) {
 287          $result = $this->doc->createAttributeNS($namespace, $name);
 288          if (!is_null($value)) {
 289              $result->nodeValue = $value;
 290          }
 291          return $result;
 292      }
 293  
 294      /**
 295       *
 296       * Create new attribute
 297       * @param string $name
 298       * @param string $value
 299       * @return DOMAttr
 300       */
 301      public function create_attribute($name, $value = null) {
 302          $result = $this->doc->createAttribute($name);
 303          if (!is_null($value)) {
 304              $result->nodeValue = $value;
 305          }
 306          return $result;
 307      }
 308  
 309      /**
 310       *
 311       * Adds new node
 312       * @param DOMNode $parentnode
 313       * @param string $namespace
 314       * @param string $name
 315       * @param string $value
 316       * @return DOMNode
 317       */
 318      public function append_new_element_ns(DOMNode &$parentnode, $namespace, $name, $value = null) {
 319          $newnode = null;
 320          if (is_null($value)) {
 321              $newnode = $this->doc->createElementNS($namespace, $name);
 322          } else {
 323              $newnode = $this->doc->createElementNS($namespace, $name, $value);
 324          }
 325          return $parentnode->appendChild($newnode);
 326      }
 327  
 328      /**
 329       *
 330       * New node with CDATA content
 331       * @param DOMNode $parentnode
 332       * @param string $namespace
 333       * @param string $name
 334       * @param string $value
 335       */
 336      public function append_new_element_ns_cdata(DOMNode &$parentnode, $namespace, $name, $value = null) {
 337          $newnode = $this->doc->createElementNS($namespace, $name);
 338          if (!is_null($value)) {
 339              $cdata = $this->doc->createCDATASection($value);
 340              $newnode->appendChild($cdata);
 341          }
 342          return $parentnode->appendChild($newnode);
 343      }
 344  
 345      /**
 346       *
 347       * Adds new node
 348       * @param DOMNode $parentnode
 349       * @param string $name
 350       * @param string $value
 351       * @return DOMNode
 352       */
 353      public function append_new_element(DOMNode &$parentnode, $name, $value = null) {
 354          $newnode = null;
 355          if (is_null($value)) {
 356              $newnode = $this->doc->createElement($name);
 357          } else {
 358              $newnode = $this->doc->createElement($name, $value);
 359          }
 360          return $parentnode->appendChild($newnode);
 361      }
 362  
 363      /**
 364       *
 365       * Adds new attribute
 366       * @param DOMNode $node
 367       * @param string $name
 368       * @param string $value
 369       * @return DOMNode
 370       */
 371      public function append_new_attribute(DOMNode &$node, $name, $value = null) {
 372          return $node->appendChild($this->create_attribute($name, $value));
 373      }
 374  
 375      /**
 376       *
 377       * Adds new attribute
 378       * @param DOMNode $node
 379       * @param string $namespace
 380       * @param string $name
 381       * @param string $value
 382       * @return DOMNode
 383       */
 384      public function append_new_attribute_ns(DOMNode &$node, $namespace, $name, $value = null) {
 385          return $node->appendChild($this->create_attribute_ns($namespace, $name, $value));
 386      }
 387  
 388      public function fileName() {
 389          return $this->filename;
 390      }
 391  
 392      public function filePath() {
 393          return $this->filepath;
 394      }
 395  
 396      protected function on_load() {
 397          return $this->isloaded;
 398      }
 399  
 400      protected function on_save() {
 401          return true;
 402      }
 403  
 404      protected function on_create() {
 405          return true;
 406      }
 407  
 408      public function resetXpath() {
 409          $this->dxpath = null;
 410          $this->chkxpath();
 411      }
 412  
 413      private function chkxpath() {
 414          if (!isset($this->dxpath) || is_null($this->dxpath)) {
 415              $this->dxpath = new DOMXPath($this->doc);
 416              foreach ($this->arrayPrefixNS as $nskey => $nsuri) {
 417                  $this->dxpath->registerNamespace($nskey, $nsuri);
 418              }
 419          }
 420      }
 421  
 422      protected function processPath() {
 423          $path_parts     = pathinfo($this->filename);
 424          $this->filepath = array_key_exists('dirname', $path_parts) ? $path_parts['dirname']."/" : '';
 425      }
 426  }