PageRenderTime 43ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/saf/lib/Misc/Ini.php

https://github.com/wokkie/sitellite
PHP | 458 lines | 210 code | 34 blank | 214 comment | 37 complexity | 60287b77d625cfa841febe2544f61588 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, Apache-2.0
  1. <?php
  2. //include_once ('../../init.php');
  3. /**
  4. * Class for parsing, transforming, and writing files in the INI format,
  5. * as defined by the PHP parse_ini_file() function.
  6. *
  7. * New in 1.2:
  8. * - Added ini_write() convenience function.
  9. * - Added ability for filters to reverse themselves, which is used to
  10. * properly write files that have been filtered on reading.
  11. *
  12. * @package Misc
  13. * @version 1.2, 2003-09-29, $Id: Ini.php,v 1.4 2008/02/17 18:55:47 lux Exp $
  14. */
  15. class Ini {
  16. var $trues = array ('on', 'On', 'yes', 'Yes', 'true', 'True');
  17. var $falses = array ('off', 'Off', 'no', 'No', 'false', 'False');
  18. /**
  19. * Constructor method.
  20. */
  21. function Ini () {
  22. $this->addFilter ('ini_filter_replace_backticks');
  23. }
  24. /**
  25. * Parses the specified file as an INI file. Please note that
  26. * this method defaults to assuming INI files _do_ have $sections,
  27. * whereas PHP's parse_ini_file() function assumes the opposite.
  28. *
  29. * @param string
  30. * @param boolean
  31. * @return array hash
  32. */
  33. function parse ($file, $sections = true) {
  34. if (! @file_exists ($file)) {
  35. $data = $this->parseStr ($file, $sections);
  36. } else {
  37. $data = @parse_ini_file ($file, $sections);
  38. }
  39. if (! $data) {
  40. return array ();
  41. }
  42. if (strtolower (get_class ($this)) == 'ini') {
  43. return $this->filter ($data, $sections);
  44. }
  45. return $data;
  46. }
  47. function parseStr ($str, $sections = true) {
  48. $section = null;
  49. $data = array ();
  50. if ($tmp = strtok ($str, "\r\n")) {
  51. do {
  52. switch ($tmp{0}) {
  53. case ';':
  54. case '#':
  55. break;
  56. case '[':
  57. if (! $sections) {
  58. break;
  59. }
  60. $pos = strpos ($tmp, '[');
  61. $section = substr ($tmp, $pos + 1, strpos ($tmp, ']', $pos) - 1);
  62. $data[$section] = array ();
  63. break;
  64. default:
  65. $pos = strpos ($tmp, '=');
  66. if ($pos === false) {
  67. break;
  68. }
  69. $name = trim (substr ($tmp, 0, $pos));
  70. $value = trim (substr ($tmp, $pos + 1), ' "');
  71. $quotes = trim (substr ($tmp, $pos + 1), ' ');
  72. if ($quotes == $value) {
  73. // no quotes, parse for literal values and constants
  74. if (in_array ($value, $this->trues)) {
  75. $value = '1';
  76. } elseif (in_array ($value, $this->falses)) {
  77. $value = '';
  78. } elseif (defined ($value)) {
  79. $value = constant ($value);
  80. }
  81. }
  82. if ($sections) {
  83. $data[$section][$name] = $value;
  84. } else {
  85. $data[$name] = $value;
  86. }
  87. break;
  88. }
  89. } while ($tmp = strtok ("\r\n"));
  90. }
  91. return $data;
  92. }
  93. /**
  94. * Adds a filter to the parse() process.
  95. *
  96. * @param string function name of the filter
  97. * @param array hash of keys to limit the filter to
  98. */
  99. function addFilter ($func, $keys = array ()) {
  100. $this->_filters[] = array ($func, $keys);
  101. }
  102. // note: if $sections is true, then there must be no initial pairs
  103. // outside of a section at the start.
  104. /**
  105. * Filters the specified data through all registered filters.
  106. *
  107. * @param array hash of parsed INI data
  108. * @param boolean
  109. * @param boolean
  110. * @return array hash of filtered INI data
  111. */
  112. function filter ($data, $sections = true, $reverse = false) {
  113. foreach ($this->_filters as $filter) {
  114. if (count ($filter[1]) > 0) { // limited keys specified
  115. foreach ($filter[1] as $k) {
  116. if ($sections) {
  117. foreach ($data as $key => $value) {
  118. if (in_array ($k, array_keys ($value))) {
  119. $data[$key][$k] = $filter[0] ($data[$key][$k], $reverse);
  120. }
  121. }
  122. } else {
  123. if (in_array ($k, array_keys ($data))) {
  124. $data[$k] = $filter[0] ($data[$k], $reverse);
  125. }
  126. }
  127. }
  128. } else { // applies to all values
  129. foreach ($data as $key => $value) {
  130. if ($sections) {
  131. foreach ($value as $k => $v) {
  132. $data[$key][$k] = $filter[0] ($v, $reverse);
  133. }
  134. } else {
  135. $data[$key] = $filter[0] ($value, $reverse);
  136. }
  137. }
  138. }
  139. }
  140. return $data;
  141. }
  142. /**
  143. * Clears the list of registered filters, and adds one filter
  144. * back to the list, ini_filter_replace_backticks, which replaces
  145. * backtick characters with double-quotes in values. This
  146. * filter allows a second level of quotes to be used within
  147. * a single INI value.
  148. */
  149. function clear () {
  150. $this->_filters = array ();
  151. $this->addFilter ('ini_filter_replace_backticks');
  152. }
  153. /**
  154. * Returns the specified INI structure as an XML document.
  155. * The root node is an &lt;INI&gt; tag, and subsequent tags
  156. * all represent key names from the $struct.
  157. *
  158. * @param array hash
  159. * @return string XML data
  160. */
  161. function toXml ($struct) {
  162. $out = "<INI>\n";
  163. foreach ($struct as $key => $value) {
  164. $out .= "\t<$key>";
  165. if (is_array ($value)) {
  166. $out .= "\n";
  167. foreach ($value as $k => $v) {
  168. $out .= "\t\t<$k>" . htmlentities ($v) . "</$k>\n";
  169. }
  170. $out .= "\t";
  171. } else {
  172. $out .= htmlentities ($value);
  173. }
  174. $out .= "</$key>\n";
  175. }
  176. $out .= "</INI>\n";
  177. return $out;
  178. }
  179. /**
  180. * Parses an XML document into an INI $struct, which can then
  181. * be saved to a file or processed further.
  182. *
  183. * @param string XML data or file name
  184. * @param boolean is the $data a file
  185. * @return array hash
  186. */
  187. function fromXml ($data, $isFile = false) {
  188. global $loader;
  189. $loader->import ('saf.XML.Sloppy');
  190. $sloppy = new SloppyDOM;
  191. if ($isFile) {
  192. $data = @join ('', @file ($data));
  193. }
  194. $doc = $sloppy->parse ($data);
  195. if (! $doc) {
  196. $this->error = $sloppy->error;
  197. return false;
  198. }
  199. $struct = array ();
  200. foreach ($doc->root->children as $child) {
  201. if (count ($child->children) > 0) {
  202. // 2-d
  203. $struct[$child->name] = array ();
  204. foreach ($child->children as $item) {
  205. $struct[$child->name][$item->name] = $item->content;
  206. }
  207. } else {
  208. // single level
  209. $struct[$child->name] = $child->content;
  210. }
  211. }
  212. return $struct;
  213. }
  214. /**
  215. * Formats an individual value from an INI file, using quotes if any invalid
  216. * characters are present.
  217. *
  218. * @param string
  219. * @return string
  220. */
  221. function writeValue ($val) {
  222. if (is_bool ($val)) {
  223. if ($val) {
  224. return 'On';
  225. }
  226. return 'Off';
  227. } elseif ($val === '0') {
  228. return 'Off';
  229. } elseif ($val === '1') {
  230. return 'On';
  231. } elseif ($val === '') {
  232. return 'Off';
  233. }
  234. if (preg_match ('/[^a-zA-Z0-9\/\.@<> _-]/', $val)) {
  235. return '"' . $val . '"';
  236. }
  237. //if (in_array ($val, $this->trues) || in_array ($val, $this->falses)) {
  238. // return '"' . $val . '"';
  239. //}
  240. return $val;
  241. }
  242. /**
  243. * Turns an INI structure into an INI formatted string, ready for writing
  244. * to a file.
  245. *
  246. * @param array hash
  247. * @param string instructions to include as comments in the data
  248. * @return string
  249. */
  250. function write ($struct, $instructions = false) {
  251. $out = "; <?php /* DO NOT ALTER THIS LINE, IT IS HERE FOR SECURITY REASONS\n;\n";
  252. if ($instructions) {
  253. foreach (preg_split ('/\n/s', $instructions) as $line) {
  254. $out .= '; ' . $line . "\n";
  255. }
  256. $out .= ";\n\n";
  257. } else {
  258. $out .= "; WARNING: This file was automatically generated, and it may\n";
  259. $out .= "; not be wise to edit it by hand. If there is an interface\n";
  260. $out .= "; to modify files of this type, please use that interface\n";
  261. $out .= "; instead of manually editing this file. If you are not sure\n";
  262. $out .= "; or are not aware of such an interface, please talk to your\n";
  263. $out .= "; Sitellite administrator first.\n";
  264. $out .= ";\n\n";
  265. }
  266. if (is_array ($struct[array_shift (array_keys ($struct))])) {
  267. $sections = true;
  268. } else {
  269. $sections = false;
  270. }
  271. // reverse-filter
  272. $struct = $this->filter ($struct, $sections, true);
  273. foreach ($struct as $key => $value) {
  274. if (is_array ($value)) {
  275. $out .= "[$key]\n\n";
  276. foreach ($value as $k => $v) {
  277. $out .= str_pad ($k, 24) . '= ' . Ini::writeValue ($v) . "\n\n";
  278. }
  279. } else {
  280. $out .= str_pad ($key, 24) . '= ' . Ini::writeValue ($value) . "\n\n";
  281. }
  282. }
  283. $out .= ";\n; THE END\n;\n; DO NOT ALTER THIS LINE, IT IS HERE FOR SECURITY REASONS */ " . CLOSE_TAG;
  284. return $out;
  285. }
  286. }
  287. // convenience functions:
  288. /**
  289. * Parses an INI file -- calls parse() on a global $ini object.
  290. *
  291. * @param string
  292. * @param boolean
  293. * @return array hash
  294. */
  295. function ini_parse ($file, $sections = true) {
  296. return $GLOBALS['ini']->parse ($file, $sections);
  297. }
  298. /**
  299. * Adds a filter to the global $ini object. Alias of addFilter() method.
  300. *
  301. * @param string
  302. * @param array
  303. */
  304. function ini_add_filter ($func, $keys = array ()) {
  305. return $GLOBALS['ini']->addFilter ($func, $keys);
  306. }
  307. /**
  308. * Filters the specified data using the global $ini object.
  309. * Alias of filter() method.
  310. *
  311. * @param array hash
  312. * @param boolean
  313. * @return array hash
  314. */
  315. function ini_filter ($data, $sections = true) {
  316. return $GLOBALS['ini']->filter ($data, $sections);
  317. }
  318. /**
  319. * Clears the list of filters in the global $ini object.
  320. * Alias of clear() method.
  321. */
  322. function ini_clear () {
  323. return $GLOBALS['ini']->clear ();
  324. }
  325. /**
  326. * Writes an INI data string from the specified structure.
  327. * Alias of write() in global $ini object.
  328. *
  329. * @param array hash
  330. * @param string
  331. * @return string
  332. */
  333. function ini_write ($struct, $instructions = false) {
  334. return $GLOBALS['ini']->write ($struct, $instructions);
  335. }
  336. // built-in event handlers:
  337. /**
  338. * Backtick INI filter. Replaces backticks (`) with double-quotes (").
  339. * Please note that this filter is on by default in the Ini class.
  340. *
  341. * The $reverse parameters tells the filter to do the reverse of its
  342. * ordinary function and return that instead. This is used by the
  343. * write() method to write back an INI file correctly that has
  344. * been filtered.
  345. *
  346. * @param string
  347. * @param boolean
  348. * @return string
  349. */
  350. function ini_filter_replace_backticks ($value, $reverse = false) {
  351. if ($reverse) {
  352. return str_replace ('"', '`', $value);
  353. }
  354. return str_replace ('`', '"', $value);
  355. }
  356. /**
  357. * Comma-splitting INI filter. Breaks a string into a list by the
  358. * commas in it. Note: An optional single space is allowed following
  359. * commas, and will be trimmed.
  360. *
  361. * The $reverse parameters tells the filter to do the reverse of its
  362. * ordinary function and return that instead. This is used by the
  363. * write() method to write back an INI file correctly that has
  364. * been filtered.
  365. *
  366. * @param string
  367. * @param boolean
  368. * @return array
  369. */
  370. function ini_filter_split_commas ($value, $reverse = false) {
  371. if ($reverse) {
  372. if (is_array ($value)) {
  373. return join (', ', $value);
  374. }
  375. return $value;
  376. }
  377. return preg_split ('/, ?/', $value);
  378. }
  379. /**
  380. * Single-comma-splitting INI filter. Breaks a string into two by
  381. * the first comma in it. Note: An optional single space is allowed
  382. * following the comma, and will be trimmed.
  383. *
  384. * The $reverse parameters tells the filter to do the reverse of its
  385. * ordinary function and return that instead. This is used by the
  386. * write() method to write back an INI file correctly that has
  387. * been filtered.
  388. *
  389. * @param string
  390. * @param boolean
  391. * @return array
  392. */
  393. function ini_filter_split_comma_single ($value, $reverse = false) {
  394. if ($reverse) {
  395. if (is_array ($value)) {
  396. return join (', ', $value);
  397. }
  398. return $value;
  399. }
  400. return preg_split ('/, ?/', $value, 2);
  401. }
  402. /* tests
  403. echo '<pre>';
  404. $struct = Ini::parse ('../../../inc/boxes/syndicate/settings.php');
  405. var_dump ($struct);
  406. echo "\n\n";
  407. echo htmlentities (Ini::toXml ($struct));
  408. echo "\n\n";
  409. echo htmlentities (Ini::write ($struct, "Instructions about\nthis file..."));
  410. echo "\n\n";
  411. echo htmlentities (Ini::write ($struct));
  412. echo "\n\n";
  413. echo htmlentities (Ini::write (Ini::fromXml (Ini::toXml ($struct))));
  414. echo "\n\n";
  415. echo htmlentities (Ini::write (Ini::fromXml (join ('', file ('../../../sitellite/mod/fortune/fortune.mod')))));
  416. echo '</pre>';
  417. */
  418. ?>