IniReader.php 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. <?php
  2. /**
  3. * IniReader
  4. *
  5. * PHP 5
  6. *
  7. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  9. *
  10. * Licensed under The MIT License
  11. * For full copyright and license information, please see the LICENSE.txt
  12. * Redistributions of files must retain the above copyright notice.
  13. *
  14. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  15. * @link http://cakephp.org CakePHP(tm) Project
  16. * @package Cake.Configure
  17. * @since CakePHP(tm) v 2.0
  18. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  19. */
  20. App::uses('Hash', 'Utility');
  21. /**
  22. * Ini file configuration engine.
  23. *
  24. * Since IniReader uses parse_ini_file underneath, you should be aware that this
  25. * class shares the same behavior, especially with regards to boolean and null values.
  26. *
  27. * In addition to the native `parse_ini_file` features, IniReader also allows you
  28. * to create nested array structures through usage of `.` delimited names. This allows
  29. * you to create nested arrays structures in an ini config file. For example:
  30. *
  31. * `db.password = secret` would turn into `array('db' => array('password' => 'secret'))`
  32. *
  33. * You can nest properties as deeply as needed using `.`'s. In addition to using `.` you
  34. * can use standard ini section notation to create nested structures:
  35. *
  36. * {{{
  37. * [section]
  38. * key = value
  39. * }}}
  40. *
  41. * Once loaded into Configure, the above would be accessed using:
  42. *
  43. * `Configure::read('section.key');
  44. *
  45. * You can combine `.` separated values with sections to create more deeply
  46. * nested structures.
  47. *
  48. * IniReader also manipulates how the special ini values of
  49. * 'yes', 'no', 'on', 'off', 'null' are handled. These values will be
  50. * converted to their boolean equivalents.
  51. *
  52. * @package Cake.Configure
  53. * @see http://php.net/parse_ini_file
  54. */
  55. class IniReader implements ConfigReaderInterface {
  56. /**
  57. * The path to read ini files from.
  58. *
  59. * @var array
  60. */
  61. protected $_path;
  62. /**
  63. * The section to read, if null all sections will be read.
  64. *
  65. * @var string
  66. */
  67. protected $_section;
  68. /**
  69. * Build and construct a new ini file parser. The parser can be used to read
  70. * ini files that are on the filesystem.
  71. *
  72. * @param string $path Path to load ini config files from. Defaults to APP . 'Config' . DS
  73. * @param string $section Only get one section, leave null to parse and fetch
  74. * all sections in the ini file.
  75. */
  76. public function __construct($path = null, $section = null) {
  77. if (!$path) {
  78. $path = APP . 'Config' . DS;
  79. }
  80. $this->_path = $path;
  81. $this->_section = $section;
  82. }
  83. /**
  84. * Read an ini file and return the results as an array.
  85. *
  86. * For backwards compatibility, acl.ini.php will be treated specially until 3.0.
  87. *
  88. * @param string $key The identifier to read from. If the key has a . it will be treated
  89. * as a plugin prefix. The chosen file must be on the reader's path.
  90. * @return array Parsed configuration values.
  91. * @throws ConfigureException when files don't exist.
  92. * Or when files contain '..' as this could lead to abusive reads.
  93. * @throws ConfigureException
  94. */
  95. public function read($key) {
  96. if (strpos($key, '..') !== false) {
  97. throw new ConfigureException(__d('cake_dev', 'Cannot load configuration files with ../ in them.'));
  98. }
  99. $file = $this->_getFilePath($key);
  100. if (!is_file($file)) {
  101. throw new ConfigureException(__d('cake_dev', 'Could not load configuration file: %s', $file));
  102. }
  103. $contents = parse_ini_file($file, true);
  104. if (!empty($this->_section) && isset($contents[$this->_section])) {
  105. $values = $this->_parseNestedValues($contents[$this->_section]);
  106. } else {
  107. $values = array();
  108. foreach ($contents as $section => $attribs) {
  109. if (is_array($attribs)) {
  110. $values[$section] = $this->_parseNestedValues($attribs);
  111. } else {
  112. $parse = $this->_parseNestedValues(array($attribs));
  113. $values[$section] = array_shift($parse);
  114. }
  115. }
  116. }
  117. return $values;
  118. }
  119. /**
  120. * parses nested values out of keys.
  121. *
  122. * @param array $values Values to be exploded.
  123. * @return array Array of values exploded
  124. */
  125. protected function _parseNestedValues($values) {
  126. foreach ($values as $key => $value) {
  127. if ($value === '1') {
  128. $value = true;
  129. }
  130. if ($value === '') {
  131. $value = false;
  132. }
  133. unset($values[$key]);
  134. if (strpos($key, '.') !== false) {
  135. $values = Hash::insert($values, $key, $value);
  136. } else {
  137. $values[$key] = $value;
  138. }
  139. }
  140. return $values;
  141. }
  142. /**
  143. * Dumps the state of Configure data into an ini formatted string.
  144. *
  145. * @param string $key The identifier to write to. If the key has a . it will be treated
  146. * as a plugin prefix.
  147. * @param array $data The data to convert to ini file.
  148. * @return int Bytes saved.
  149. */
  150. public function dump($key, $data) {
  151. $result = array();
  152. foreach ($data as $k => $value) {
  153. $isSection = false;
  154. if ($k[0] !== '[') {
  155. $result[] = "[$k]";
  156. $isSection = true;
  157. }
  158. if (is_array($value)) {
  159. $kValues = Hash::flatten($value, '.');
  160. foreach ($kValues as $k2 => $v) {
  161. $result[] = "$k2 = " . $this->_value($v);
  162. }
  163. }
  164. if ($isSection) {
  165. $result[] = '';
  166. }
  167. }
  168. $contents = trim(implode("\n", $result));
  169. $filename = $this->_getFilePath($key);
  170. return file_put_contents($filename, $contents);
  171. }
  172. /**
  173. * Converts a value into the ini equivalent
  174. *
  175. * @param mixed $value to export.
  176. * @return string String value for ini file.
  177. */
  178. protected function _value($val) {
  179. if ($val === null) {
  180. return 'null';
  181. }
  182. if ($val === true) {
  183. return 'true';
  184. }
  185. if ($val === false) {
  186. return 'false';
  187. }
  188. return (string)$val;
  189. }
  190. /**
  191. * Get file path
  192. *
  193. * @param string $key The identifier to write to. If the key has a . it will be treated
  194. * as a plugin prefix.
  195. * @return string Full file path
  196. */
  197. protected function _getFilePath($key) {
  198. if (substr($key, -8) === '.ini.php') {
  199. $key = substr($key, 0, -8);
  200. list($plugin, $key) = pluginSplit($key);
  201. $key .= '.ini.php';
  202. } else {
  203. if (substr($key, -4) === '.ini') {
  204. $key = substr($key, 0, -4);
  205. }
  206. list($plugin, $key) = pluginSplit($key);
  207. $key .= '.ini';
  208. }
  209. if ($plugin) {
  210. $file = App::pluginPath($plugin) . 'Config' . DS . $key;
  211. } else {
  212. $file = $this->_path . $key;
  213. }
  214. return $file;
  215. }
  216. }