1<?php 2 3/* 4 This file is part of ActiveLink PHP XML Package (www.active-link.com). 5 Copyright (c) 2002-2004 by Zurab Davitiani 6 7 You can contact the author of this software via E-mail at 8 hattrick@mailcan.com 9 10 ActiveLink PHP XML Package is free software; you can redistribute it and/or modify 11 it under the terms of the GNU Lesser General Public License as published by 12 the Free Software Foundation; either version 2.1 of the License, or 13 (at your option) any later version. 14 15 ActiveLink PHP XML Package is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU Lesser General Public License for more details. 19 20 You should have received a copy of the GNU Lesser General Public License 21 along with ActiveLink PHP XML Package; if not, write to the Free Software 22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23*/ 24 25import("org.active-link.xml.Tag"); 26import("org.active-link.xml.Tree"); 27 28/** 29 * XML class provides a tree-like structure to read/write/modify XML 30 * @class XML 31 * @package org.active-link.xml 32 * @author Zurab Davitiani 33 * @version 0.4.0 34 * @extends Tree 35 * @requires Tag, Tree, XMLBranch, XMLLeaf 36 * @see Tree 37 */ 38 39class XML extends Tree { 40 41 // protected variables 42 var $tag; 43 var $pathSeparator; 44 45 /** 46 * If argument is an XML String it parses the string into XML object 47 * If argument is a tag path, creates appropriate branches and tags 48 * If argument is a simple string then sets that as a root tag name 49 * @method XML 50 * @param optional string argument 51 * @returns none 52 */ 53 function XML($argument = "") { 54 $success = false; 55 $this->Tree(); 56 $this->pathSeparator = "/"; 57 $this->tag = new Tag(); 58 if(is_string($argument)) { 59 // if this is an XML string to be parsed 60 if(strpos($argument, $this->tag->tagEndOpen) > 0 || strpos($argument, $this->tag->tagClose) > 0) 61 $this->parseFromString($argument); 62 // else if this is a tag path to be created 63 elseif(strpos($argument, $this->pathSeparator) > 0) { 64 $tags = explode($this->pathSeparator, $argument); 65 $this->tag->setTagName($tags[0]); 66 $this->setTagContent("", $argument); 67 } 68 else 69 $this->tag->setTagName($argument); 70 $success = true; 71 } 72 else 73 $success = false; 74 return $success; 75 } 76 77 /** 78 * Adds another XML tree as a branch to the current XML object 79 * @method addXMLAsBranch 80 * @param object xml 81 * @param optional mixed id 82 * @returns true if successful, false otherwise 83 */ 84 function addXMLAsBranch($xml, $id = -1) { 85 $success = false; 86 if(is_object($xml) && strtolower(get_class($xml)) == "xml") { 87 $newBranch = new XMLBranch(); 88 $newBranch->nodes = $xml->nodes; 89 $newBranch->tag = $xml->tag; 90 $success = $this->addXMLBranch($newBranch, $id); 91 } 92 return $success; 93 } 94 95 /** 96 * Adds XML Branch to the current XML object 97 * @method addXMLBranch 98 * @param object xmlBranch 99 * @param optional mixed id 100 * @returns true if successful, false otherwise 101 */ 102 function addXMLBranch($xmlBranch, $id = -1) { 103 $success = false; 104 if(is_object($xmlBranch) && strtolower(get_class($xmlBranch)) == "xmlbranch") { 105 $xmlBranch->setParentXML($this); 106 $success = $this->addNode($id, $xmlBranch); 107 } 108 return $success; 109 } 110 111 /** 112 * Adds XML Leaf to the current XML object 113 * @method addXMLLeaf 114 * @param object xmlLeaf 115 * @param optional mixed id 116 * @returns true if successful, false otherwise 117 */ 118 function addXMLLeaf($xmlLeaf, $id = -1) { 119 $success = false; 120 if(is_object($xmlLeaf) && strtolower(get_class($xmlLeaf)) == "xmlleaf") { 121 $xmlLeaf->setParentXML($this); 122 $success = $this->addNode($id, $xmlLeaf); 123 } 124 return $success; 125 } 126 127 /** 128 * Retrieves an array of references to XMLBranches within the specified path, tag name, attribute name, and attribute value 129 * @method getBranches 130 * @param optional string tagPath 131 * @param optional string tagName 132 * @param optional string attrName 133 * @param optional string attrValue 134 * @returns array of references to XMLBranch objects that meet specified criteria, or false if none found 135 */ 136 function getBranches($tagPath = "", $tagName = "", $attrName = "", $attrValue = "") { 137 $branchArray = array(); 138 if($tagPath == "") 139 $tagPath = $this->tag->getTagName(); 140 $tags = explode($this->pathSeparator, $tagPath); 141 if($this->tag->getTagName() == $tags[0]) { 142 if(count($tags) == 1) { 143 $arrKeys = array_keys($this->nodes); 144 for($index = 0; $index < count($arrKeys); $index ++) { 145 if(gettype($this->nodes[$arrKeys[$index]]) == "object" && strtolower(get_class($this->nodes[$arrKeys[$index]])) == "xmlbranch") { 146 if(($tagName == "" || $this->nodes[$arrKeys[$index]]->tag->getTagName() == $tagName) && 147 ($attrName == "" || $this->nodes[$arrKeys[$index]]->tag->attributeExists($attrName)) && 148 ($attrValue == "" || $this->nodes[$arrKeys[$index]]->tag->getTagAttribute($attrName) == $attrValue)) { 149 $branchArray[] = &$this->nodes[$arrKeys[$index]]; 150 } 151 } 152 } 153 } 154 else { 155 $arrKeys = array_keys($this->nodes); 156 for($index = 0; $index < count($arrKeys); $index ++) { 157 if(gettype($this->nodes[$arrKeys[$index]]) == "object" && strtolower(get_class($this->nodes[$arrKeys[$index]])) == "xmlbranch") { 158 if($this->nodes[$arrKeys[$index]]->tag->getTagName() == $tags[1]) { 159 $newTagPath = implode($this->pathSeparator, array_slice($tags, 1)); 160 $newArray = $this->nodes[$arrKeys[$index]]->getBranches($newTagPath, $tagName, $attrName, $attrValue); 161 if($newArray !== false) 162 $branchArray = array_merge($branchArray, $newArray); 163 } 164 } 165 } 166 } 167 } 168 if(count($branchArray) == 0) 169 $branchArray = false; 170 return $branchArray; 171 } 172 173 /** 174 * Retrieves an array of references to XMLLeaf(s) within the specified path 175 * @method getLeafs 176 * @param optional string tagPath 177 * @returns array of references to XMLLeaf objects in specified tag path, false if none found 178 */ 179 function getLeafs($tagPath = "") { 180 $leafArray = array(); 181 if($tagPath == "") 182 $tagPath = $this->tag->getTagName(); 183 $tags = explode($this->pathSeparator, $tagPath); 184 if($this->tag->getTagName() == $tags[0]) { 185 if(count($tags) == 1) { 186 $arrKeys = array_keys($this->nodes); 187 for($index = 0; $index < count($arrKeys); $index ++) { 188 if(gettype($this->nodes[$arrKeys[$index]]) == "object" && strtolower(get_class($this->nodes[$arrKeys[$index]])) == "xmlleaf") { 189 $leafArray[] = &$this->nodes[$arrKeys[$index]]; 190 } 191 } 192 } 193 else { 194 $arrKeys = array_keys($this->nodes); 195 for($index = 0; $index < count($arrKeys); $index ++) { 196 if(gettype($this->nodes[$arrKeys[$index]]) == "object" && strtolower(get_class($this->nodes[$arrKeys[$index]])) == "xmlbranch") { 197 if($this->nodes[$arrKeys[$index]]->tag->getTagName() == $tags[1]) { 198 $newTagPath = implode($this->pathSeparator, array_slice($tags, 1)); 199 $newArray = $this->nodes[$arrKeys[$index]]->getLeafs($newTagPath); 200 if($newArray !== false) 201 $leafArray = array_merge($leafArray, $newArray); 202 } 203 } 204 } 205 } 206 } 207 if(count($leafArray) == 0) 208 $leafArray = false; 209 return $leafArray; 210 } 211 212 /** 213 * Returns attribute value of the specified tag and tagpath 214 * @method getTagAttribute 215 * @param string attributeName 216 * @param optional string tagPath 217 * @returns attribute of the specified tag if successful, false otherwise 218 */ 219 function getTagAttribute($attributeName, $tagPath = "") { 220 if($tagPath == "") 221 $tagPath = $this->tag->getTagName(); 222 $tags = explode($this->pathSeparator, $tagPath); 223 $attributeValue = false; 224 if($this->tag->getTagName() == $tags[0]) { 225 if(sizeof($tags) == 1) { 226 if($this->tag->attributeExists($attributeName)) 227 $attributeValue = $this->tag->getTagAttribute($attributeName); 228 } 229 else { 230 foreach($this->nodes as $node) { 231 if(strtolower(get_class($node)) == "xmlbranch") 232 if($node->tag->getTagName() == $tags[1]) { 233 $newTagPath = implode($this->pathSeparator, array_slice($tags, 1)); 234 $attributeValue = $node->getTagAttribute($attributeName, $newTagPath); 235 } 236 } 237 } 238 } 239 return $attributeValue; 240 } 241 242 /** 243 * Returns contents of the specified tag path 244 * @method getTagContent 245 * @param optional string tagPath 246 * @returns content of the tag from the specified path if successful, false otherwise 247 */ 248 function getTagContent($tagPath = "") { 249 if($tagPath == "") 250 $tagPath = $this->tag->getTagName(); 251 $tags = explode($this->pathSeparator, $tagPath); 252 $tagValue = false; 253 if($this->tag->getTagName() == $tags[0]) { 254 if(sizeof($tags) == 1) 255 $tagValue = $this->getXMLContent(); 256 else { 257 foreach($this->nodes as $node) { 258 if(strtolower(get_class($node)) == "xmlbranch") 259 if($node->tag->getTagName() == $tags[1]) { 260 $newTagPath = implode($this->pathSeparator, array_slice($tags, 1)); 261 $tagValue = $node->getTagContent($newTagPath); 262 } 263 } 264 } 265 } 266 return $tagValue; 267 } 268 269 /** 270 * Retrieves the tag name of the current object 271 * @method getTagName 272 * @returns tag name 273 */ 274 function getTagName() { 275 return($this->tag->getTagName()); 276 } 277 278 /** 279 * Gets contents from the current object 280 * @method getXMLContent 281 * @returns contents of the current XML tag 282 */ 283 function getXMLContent() { 284 $xmlContent = ""; 285 foreach($this->nodes as $node) { 286 if(gettype($node) == "object") { 287 if(strtolower(get_class($node)) == "xmlbranch") 288 $xmlContent .= $node->getXMLString(); 289 elseif(strtolower(get_class($node)) == "xmlleaf") 290 $xmlContent .= $node->getValue(); 291 } 292 } 293 return $xmlContent; 294 } 295 296 /** 297 * Gets the whole XML string of the current object 298 * @method getXMLString 299 * @param optional mixed indent 300 * @returns complete XML string of current object 301 */ 302 function getXMLString($indent = false) { 303 $xmlString = ""; 304 $containsBranches = false; 305 $containsLeafs = false; 306 $newIndent = false; 307 if($indent === false) 308 $newIndent = false; 309 else { 310 $newIndent = $indent + 1; 311 $this->tag->setTagFormat($this->tag->FORMAT_INDENT, $indent); 312 } 313 foreach($this->nodes as $node) { 314 if(gettype($node) == "object") { 315 if(strtolower(get_class($node)) == "xmlbranch") { 316 $this->tag->tagContent .= $node->getXMLString($newIndent); 317 $containsBranches = true; 318 } 319 elseif(strtolower(get_class($node)) == "xmlleaf") { 320 $this->tag->tagContent .= $node->getValue(); 321 $containsLeafs = true; 322 } 323 } 324 } 325 if($containsBranches) 326 $this->tag->setTagFormatEndTag(true); 327 $xmlString = $this->tag->getTagString(); 328 $this->tag->setTagContent(""); 329 return $xmlString; 330 } 331 332 /** 333 * Find out whether the current object has any branches 334 * @method hasBranch 335 * @returns true if branches exist, false otherwise 336 */ 337 function hasBranch() { 338 $hasBranch = false; 339 foreach($this->nodes as $node) { 340 if(strtolower(get_class($node)) == "xmlbranch") { 341 $hasBranch = true; 342 break; 343 } 344 } 345 return $hasBranch; 346 } 347 348 /** 349 * Find out whether the current object has any leaf(s) 350 * @method hasLeaf 351 * @returns true if leaf(s) exist, false otherwise 352 */ 353 function hasLeaf() { 354 $hasLeaf = false; 355 foreach($this->nodes as $node) { 356 if(strtolower(get_class($node)) == "xmlleaf") { 357 $hasLeaf = true; 358 break; 359 } 360 } 361 return $hasLeaf; 362 } 363 364 /** 365 * Parse entire XML string into the current object; also called from constructor 366 * @method parseFromString 367 * @param string parseString 368 * @returns none 369 */ 370 function parseFromString($parseString) { 371 $tagResult = $this->tag->setTagFromString($parseString); 372 if($tagResult !== false) { 373 $this->parseNodesFromTag(); 374 $this->tag->setTagContent(""); 375 } 376 } 377 378 /** 379 * Parses the current tag content into Branches and Leaf(s); called from parseFromString 380 * @method parseNodesFromTag 381 * @returns none 382 */ 383 function parseNodesFromTag() { 384 $tempTag = new Tag(); 385 $parseString = $this->tag->getTagContent(); 386 while($tagParsed = $tempTag->setTagFromString($parseString)) { 387 if($tagParsed[0] != 0 && substr($parseString, 0, $tagParsed[0]) != "") 388 $this->addXMLLeaf(new XMLLeaf(substr($parseString, 0, $tagParsed[0]))); 389 $branch = new XMLBranch(); 390 $tempTagCopy = new Tag(); 391 $tempTagCopy->setTagName($tempTag->getTagName()); 392 $tempTagCopy->tagAttributes = $tempTag->tagAttributes; 393 $tempTagCopy->setTagContent($tempTag->getTagContent()); 394 $branch->setTag($tempTagCopy); 395 $branch->parseNodesFromTag(); 396 $branch->tag->setTagContent(""); 397 $this->addXMLBranch($branch); 398 $parseString = substr($parseString, $tagParsed[1]); 399 } 400 if(strlen($parseString) > 0 && $parseString != "") 401 $this->addXMLLeaf(new XMLLeaf($parseString)); 402 } 403 404 /** 405 * Removes all Branches from current object 406 * @method removeAllBranches 407 */ 408 function removeAllBranches() { 409 foreach($this->nodes as $key => $value) { 410 if(strtolower(get_class($value)) == "xmlbranch") 411 unset($this->nodes[$key]); 412 } 413 } 414 415 /** 416 * Removes all Leaf(s) from current object 417 * @method removeAllLeafs 418 */ 419 function removeAllLeafs() { 420 foreach($this->nodes as $key => $value) { 421 if(strtolower(get_class($value)) == "xmlleaf") 422 unset($this->nodes[$key]); 423 } 424 } 425 426 /** 427 * Removes Branches with the specified criteria 428 * @method removeBranches 429 * @param optional string tagPath 430 * @param optional string tagName 431 * @param optional string attrName 432 * @param optional string attrValue 433 * @returns number of branches deleted 434 */ 435 function removeBranches($tagPath = "", $tagName = "", $attrName = "", $attrValue = "") { 436 $branchesDeleted = 0; 437 $referencedBranches = array(); 438 $tags = explode($this->pathSeparator, $tagPath); 439 if(count($tags) > 1) { 440 $parentTagName = array_pop($tags); 441 $parentTagPath = implode($this->pathSeparator, $tags); 442 $referencedBranches = $this->getBranches($parentTagPath, $parentTagName); 443 } 444 else { 445 $referencedBranches[] = &$this; 446 } 447 for($i = 0; $i < count($referencedBranches); $i ++) { 448 $arrKeys = array_keys($referencedBranches[$i]->nodes); 449 for($index = 0; $index < count($arrKeys); $index ++) { 450 if(gettype($referencedBranches[$i]->nodes[$arrKeys[$index]]) == "object" && strtolower(get_class($referencedBranches[$i]->nodes[$arrKeys[$index]])) == "xmlbranch") { 451 if(($tagName == "" || $referencedBranches[$i]->nodes[$arrKeys[$index]]->tag->getTagName() == $tagName) && 452 ($attrName == "" || $referencedBranches[$i]->nodes[$arrKeys[$index]]->tag->attributeExists($attrName)) && 453 ($attrValue == "" || $referencedBranches[$i]->nodes[$arrKeys[$index]]->tag->getTagAttribute($attrName) == $attrValue)) { 454 $referencedBranches[$i]->removeNode($arrKeys[$index]); 455 $branchesDeleted ++; 456 } 457 } 458 } 459 } 460 return $branchesDeleted; 461 } 462 463 /** 464 * Sets tag object of a branch specified by branch ID for the current object; see getBranches and setTag 465 * @method setBranchTag 466 * @param mixed branchId 467 * @param object tag 468 * @returns true on success, false otherwise 469 */ 470 function setBranchTag($branchId, $tag) { 471 $success = true; 472 if(strtolower(get_class($this->nodes[$branchId])) == "xmlbranch" && strtolower(get_class($tag)) == "tag") 473 $this->nodes[$branchId]->setTag($tag); 474 else 475 $success = false; 476 return $success; 477 } 478 479 /** 480 * Sets tag object of the current object 481 * @method setTag 482 * @param object tag 483 * @returns true if successful, false otherwise 484 */ 485 function setTag($tag) { 486 $success = true; 487 if(strtolower(get_class($tag)) == "tag") 488 $this->tag = $tag; 489 else 490 $success = false; 491 return $success; 492 } 493 494 /** 495 * Sets an attribute name and value on an existing tag found via tagpath string 496 * @method setTagAttribute 497 * @param string attributeName 498 * @param optional string attributeValue 499 * @param optional string tagPath 500 * @returns true if successful, false otherwise 501 */ 502 function setTagAttribute($attributeName, $attributeValue = "", $tagPath = "") { 503 if($tagPath == "") 504 $tagPath = $this->tag->getTagName(); 505 $success = true; 506 $tags = explode($this->pathSeparator, $tagPath); 507 if($this->tag->getTagName() == $tags[0]) { 508 if(sizeof($tags) == 1) 509 $this->tag->setAttribute($attributeName, $attributeValue); 510 else { 511 $nodeTagFound = false; 512 reset($this->nodes); 513 $arrKeys = array_keys($this->nodes); 514 for($index = 0; $index < count($arrKeys); $index ++) { 515 $node =& $this->nodes[$arrKeys[$index]]; 516 if(strtolower(get_class($node)) == "xmlbranch") 517 if($node->tag->getTagName() == $tags[1]) { 518 $newTagPath = implode($this->pathSeparator, array_slice($tags, 1)); 519 $success = $node->setTagAttribute($attributeName, $attributeValue, $newTagPath); 520 $nodeTagFound = true; 521 } 522 } 523 if(!$nodeTagFound) 524 $success = false; 525 } 526 } 527 else 528 $success = false; 529 return $success; 530 } 531 532 /** 533 * Sets content of the specified tag 534 * @method setTagContent 535 * @param mixed content 536 * @param optional string tagPath 537 * @returns true if successful, false otherwise 538 */ 539 function setTagContent($content, $tagPath = "") { 540 if($tagPath == "") 541 $tagPath = $this->tag->getTagName(); 542 $success = true; 543 $tags = explode($this->pathSeparator, $tagPath); 544 if($this->tag->getTagName() == $tags[0]) { 545 if(sizeof($tags) == 1) { 546 //$this->nodes = array(new XMLLeaf($content)); 547 $this->removeAllNodes(); 548 $this->addXMLLeaf(new XMLLeaf($content)); 549 } 550 else { 551 $nodeTagFound = false; 552 reset($this->nodes); 553 $arrKeys = array_keys($this->nodes); 554 for($index = 0; $index < count($arrKeys); $index ++) { 555 $node =& $this->nodes[$arrKeys[$index]]; 556 if(strtolower(get_class($node)) == "xmlbranch") 557 if($node->tag->getTagName() == $tags[1]) { 558 $newTagPath = implode($this->pathSeparator, array_slice($tags, 1)); 559 $success = $node->setTagContent($content, $newTagPath); 560 $nodeTagFound = true; 561 } 562 } 563 if(!$nodeTagFound) { 564 $branch = new XMLBranch(); 565 $branch->setTag(new Tag($tags[1])); 566 $newTagPath = implode($this->pathSeparator, array_slice($tags, 1)); 567 $branch->setTagContent($content, $newTagPath); 568 $this->addXMLBranch($branch); 569 } 570 } 571 } 572 return $success; 573 } 574 575} 576 577import("org.active-link.xml.XMLBranch"); 578import("org.active-link.xml.XMLLeaf"); 579 580?> 581