1: <?php
2: /**
3: * Handles the XML contents list.
4: *
5: * PHP version 5
6: *
7: * @category Horde
8: * @package Pear
9: * @author Gunnar Wrobel <wrobel@pardus.de>
10: * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
11: * @link http://pear.horde.org/index.php?package=Pear
12: */
13:
14: /**
15: * Handles the XML contents list.
16: *
17: * Copyright 2011-2012 Horde LLC (http://www.horde.org/)
18: *
19: * See the enclosed file COPYING for license information (LGPL). If you
20: * did not receive this file, see http://www.horde.org/licenses/lgpl21.
21: *
22: * @category Horde
23: * @package Pear
24: * @author Gunnar Wrobel <wrobel@pardus.de>
25: * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
26: * @link http://pear.horde.org/index.php?package=Pear
27: */
28: class Horde_Pear_Package_Xml_Contents
29: {
30: /**
31: * The package.xml handler to operate on.
32: *
33: * @var Horde_Pear_Package_Xml
34: */
35: private $_xml;
36:
37: /**
38: * The root of the file list section.
39: *
40: * @var DOMNode
41: */
42: private $_filelist;
43:
44: /**
45: * The list of directories in the contents section.
46: *
47: * @var Horde_Pear_Package_Xml_Directory
48: */
49: private $_dir_list;
50:
51: /**
52: * The list of files in the "filelist" section.
53: *
54: * @var array
55: */
56: private $_install_list = array();
57:
58: /**
59: * Constructor.
60: *
61: * @param Horde_Pear_Package_Xml $xml The package.xml handler
62: * to operate on.
63: * @param DOMNode $contents The root node for the
64: * "contents" listing.
65: * @param DOMNode $filelist The root node for the
66: * "filelist" listing.
67: */
68: public function __construct(Horde_Pear_Package_Xml $xml, DOMNode $contents,
69: DOMNode $filelist)
70: {
71: $this->_xml = $xml;
72: $this->_filelist = $filelist;
73: $element = $this->_xml->createElementDirectory('/');
74: $element->setDocument($this->_xml);
75: $element->setDirectoryNode($contents);
76: $this->_dir_list = $this->_xml->createDirectory($element, $this->_xml);
77: $this->_populateFileList();
78: }
79:
80: /**
81: * Populate the existing file list from the XML.
82: *
83: * @param DOMNode $filelist The root node of the file list.
84: *
85: * @return NULL
86: */
87: private function _populateFileList()
88: {
89: foreach ($this->_xml->findNodesRelativeTo('./p:install', $this->_filelist) as $file) {
90: $this->_install_list['/' . $file->getAttribute('name')] = $file;
91: }
92: }
93:
94: /**
95: * Update the file list.
96: *
97: * @param array $files The new file list.
98: *
99: * @return NULL
100: */
101: public function update(Horde_Pear_Package_Contents $contents)
102: {
103: $files = $contents->getContents();
104: $added = array_diff(array_keys($files), $this->_dir_list->getFiles());
105: $deleted = array_diff($this->_dir_list->getFiles(), array_keys($files));
106: foreach ($added as $file) {
107: $this->add($file, $files[$file]);
108: }
109: foreach ($deleted as $file) {
110: $this->delete($file);
111: }
112: }
113:
114: /**
115: * Add a file to the list.
116: *
117: * @param string $file The file name.
118: * @param array $params Additional file parameters.
119: *
120: * @return NULL
121: */
122: public function add($file, $params)
123: {
124: $this->_dir_list->addFile($file, $params);
125: if (!in_array($file, array_keys($this->_install_list))) {
126: $point = $this->_getInstallInsertionPoint($file);
127: if ($point === null) {
128: $point = $this->_filelist->lastChild;
129: } else {
130: if ($point->previousSibling) {
131: $ws = trim($point->previousSibling->textContent);
132: if (empty($ws)) {
133: $point = $point->previousSibling;
134: }
135: }
136: }
137: $this->_install_list[$file] = $this->_xml->insert(
138: array(
139: "\n ",
140: 'install' => array(
141: 'as' => $params['as'], 'name' => substr($file, 1)
142: ),
143: ),
144: $point
145: );
146: }
147: }
148:
149: /**
150: * Identify the insertion point for a new file.
151: *
152: * @param string $new The key for the new element.
153: *
154: * @return mixed The insertion point.
155: */
156: private function _getInstallInsertionPoint($new)
157: {
158: $keys = array_keys($this->_install_list);
159: array_push($keys, $new);
160: usort($keys, array($this, '_fileOrder'));
161: $pos = array_search($new, $keys);
162: if ($pos < (count($keys) - 1)) {
163: return $this->_install_list[$keys[$pos + 1]];
164: } else {
165: return null;
166: }
167: }
168:
169: /**
170: * Sort order for files in the installation list.
171: *
172: * @param string $a First path.
173: * @param string $b Second path.
174: *
175: * @return int Sort comparison result.
176: */
177: private function _fileOrder($a, $b)
178: {
179: $ea = explode('/', $a);
180: $eb = explode('/', $b);
181: while (true) {
182: $pa = array_shift($ea);
183: $pb = array_shift($eb);
184: if ($pa != $pb) {
185: if ((count($ea) == 0 || count($eb) == 0)
186: && count($ea) != count($eb)) {
187: return count($ea) < count($eb) ? -1 : 1;
188: }
189: return strnatcasecmp($pa, $pb);
190: }
191: }
192: }
193:
194: /**
195: * Delete a file from the list.
196: *
197: * @param string $file The file name.
198: *
199: * @return NULL
200: */
201: public function delete($file)
202: {
203: $this->_dir_list->deleteFile($file);
204: if (isset($this->_install_list[$file])) {
205: $this->_xml->removeWhitespace(
206: $this->_install_list[$file]->nextSibling
207: );
208: $this->_filelist->removeChild($this->_install_list[$file]);
209: }
210: }
211: }