You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

580 lines
18 KiB

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