1<?php 2/** 3 * Zend Framework (http://framework.zend.com/) 4 * 5 * @link http://github.com/zendframework/zf2 for the canonical source repository 6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 7 * @license http://framework.zend.com/license/new-bsd New BSD License 8 */ 9 10namespace Zend\Config\Writer; 11 12use Zend\Config\Exception; 13 14class Ini extends AbstractWriter 15{ 16 /** 17 * Separator for nesting levels of configuration data identifiers. 18 * 19 * @var string 20 */ 21 protected $nestSeparator = '.'; 22 23 /** 24 * If true the INI string is rendered in the global namespace without 25 * sections. 26 * 27 * @var bool 28 */ 29 protected $renderWithoutSections = false; 30 31 /** 32 * Set nest separator. 33 * 34 * @param string $separator 35 * @return self 36 */ 37 public function setNestSeparator($separator) 38 { 39 $this->nestSeparator = $separator; 40 return $this; 41 } 42 43 /** 44 * Get nest separator. 45 * 46 * @return string 47 */ 48 public function getNestSeparator() 49 { 50 return $this->nestSeparator; 51 } 52 53 /** 54 * Set if rendering should occur without sections or not. 55 * 56 * If set to true, the INI file is rendered without sections completely 57 * into the global namespace of the INI file. 58 * 59 * @param bool $withoutSections 60 * @return Ini 61 */ 62 public function setRenderWithoutSectionsFlags($withoutSections) 63 { 64 $this->renderWithoutSections = (bool) $withoutSections; 65 return $this; 66 } 67 68 /** 69 * Return whether the writer should render without sections. 70 * 71 * @return bool 72 */ 73 public function shouldRenderWithoutSections() 74 { 75 return $this->renderWithoutSections; 76 } 77 78 /** 79 * processConfig(): defined by AbstractWriter. 80 * 81 * @param array $config 82 * @return string 83 */ 84 public function processConfig(array $config) 85 { 86 $iniString = ''; 87 88 if ($this->shouldRenderWithoutSections()) { 89 $iniString .= $this->addBranch($config); 90 } else { 91 $config = $this->sortRootElements($config); 92 93 foreach ($config as $sectionName => $data) { 94 if (!is_array($data)) { 95 $iniString .= $sectionName 96 . ' = ' 97 . $this->prepareValue($data) 98 . "\n"; 99 } else { 100 $iniString .= '[' . $sectionName . ']' . "\n" 101 . $this->addBranch($data) 102 . "\n"; 103 } 104 } 105 } 106 107 return $iniString; 108 } 109 110 /** 111 * Add a branch to an INI string recursively. 112 * 113 * @param array $config 114 * @param array $parents 115 * @return string 116 */ 117 protected function addBranch(array $config, $parents = array()) 118 { 119 $iniString = ''; 120 121 foreach ($config as $key => $value) { 122 $group = array_merge($parents, array($key)); 123 124 if (is_array($value)) { 125 $iniString .= $this->addBranch($value, $group); 126 } else { 127 $iniString .= implode($this->nestSeparator, $group) 128 . ' = ' 129 . $this->prepareValue($value) 130 . "\n"; 131 } 132 } 133 134 return $iniString; 135 } 136 137 /** 138 * Prepare a value for INI. 139 * 140 * @param mixed $value 141 * @return string 142 * @throws Exception\RuntimeException 143 */ 144 protected function prepareValue($value) 145 { 146 if (is_int($value) || is_float($value)) { 147 return $value; 148 } elseif (is_bool($value)) { 149 return ($value ? 'true' : 'false'); 150 } elseif (false === strpos($value, '"')) { 151 return '"' . $value . '"'; 152 } else { 153 throw new Exception\RuntimeException('Value can not contain double quotes'); 154 } 155 } 156 157 /** 158 * Root elements that are not assigned to any section needs to be on the 159 * top of config. 160 * 161 * @param array $config 162 * @return array 163 */ 164 protected function sortRootElements(array $config) 165 { 166 $sections = array(); 167 168 // Remove sections from config array. 169 foreach ($config as $key => $value) { 170 if (is_array($value)) { 171 $sections[$key] = $value; 172 unset($config[$key]); 173 } 174 } 175 176 // Read sections to the end. 177 foreach ($sections as $key => $value) { 178 $config[$key] = $value; 179 } 180 181 return $config; 182 } 183} 184