JsonConfigSource.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. <?php
  2. /*
  3. * This file is part of Composer.
  4. *
  5. * (c) Nils Adermann <naderman@naderman.de>
  6. * Jordi Boggiano <j.boggiano@seld.be>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Composer\Config;
  12. use Composer\Json\JsonFile;
  13. use Composer\Json\JsonManipulator;
  14. use Composer\Util\Silencer;
  15. /**
  16. * JSON Configuration Source
  17. *
  18. * @author Jordi Boggiano <j.boggiano@seld.be>
  19. * @author Beau Simensen <beau@dflydev.com>
  20. */
  21. class JsonConfigSource implements ConfigSourceInterface
  22. {
  23. /**
  24. * @var JsonFile
  25. */
  26. private $file;
  27. /**
  28. * @var bool
  29. */
  30. private $authConfig;
  31. /**
  32. * Constructor
  33. *
  34. * @param JsonFile $file
  35. * @param bool $authConfig
  36. */
  37. public function __construct(JsonFile $file, $authConfig = false)
  38. {
  39. $this->file = $file;
  40. $this->authConfig = $authConfig;
  41. }
  42. /**
  43. * {@inheritdoc}
  44. */
  45. public function getName()
  46. {
  47. return $this->file->getPath();
  48. }
  49. /**
  50. * {@inheritdoc}
  51. */
  52. public function addRepository($name, $config)
  53. {
  54. $this->manipulateJson('addRepository', $name, $config, function (&$config, $repo, $repoConfig) {
  55. // if converting from an array format to hashmap format, and there is a {"packagist.org":false} repo, we have
  56. // to convert it to "packagist.org": false key on the hashmap otherwise it fails schema validation
  57. if (isset($config['repositories'])) {
  58. foreach ($config['repositories'] as $index => $val) {
  59. if ($index === $repo) {
  60. continue;
  61. }
  62. if (is_numeric($index) && ($val === array('packagist' => false) || $val === array('packagist.org' => false))) {
  63. unset($config['repositories'][$index]);
  64. $config['repositories']['packagist.org'] = false;
  65. break;
  66. }
  67. }
  68. }
  69. $config['repositories'][$repo] = $repoConfig;
  70. });
  71. }
  72. /**
  73. * {@inheritdoc}
  74. */
  75. public function removeRepository($name)
  76. {
  77. $this->manipulateJson('removeRepository', $name, function (&$config, $repo) {
  78. unset($config['repositories'][$repo]);
  79. });
  80. }
  81. /**
  82. * {@inheritdoc}
  83. */
  84. public function addConfigSetting($name, $value)
  85. {
  86. $authConfig = $this->authConfig;
  87. $this->manipulateJson('addConfigSetting', $name, $value, function (&$config, $key, $val) use ($authConfig) {
  88. if (preg_match('{^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|http-basic|platform)\.}', $key)) {
  89. list($key, $host) = explode('.', $key, 2);
  90. if ($authConfig) {
  91. $config[$key][$host] = $val;
  92. } else {
  93. $config['config'][$key][$host] = $val;
  94. }
  95. } else {
  96. $config['config'][$key] = $val;
  97. }
  98. });
  99. }
  100. /**
  101. * {@inheritdoc}
  102. */
  103. public function removeConfigSetting($name)
  104. {
  105. $authConfig = $this->authConfig;
  106. $this->manipulateJson('removeConfigSetting', $name, function (&$config, $key) use ($authConfig) {
  107. if (preg_match('{^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|http-basic|platform)\.}', $key)) {
  108. list($key, $host) = explode('.', $key, 2);
  109. if ($authConfig) {
  110. unset($config[$key][$host]);
  111. } else {
  112. unset($config['config'][$key][$host]);
  113. }
  114. } else {
  115. unset($config['config'][$key]);
  116. }
  117. });
  118. }
  119. /**
  120. * {@inheritdoc}
  121. */
  122. public function addProperty($name, $value)
  123. {
  124. $this->manipulateJson('addProperty', $name, $value, function (&$config, $key, $val) {
  125. if (substr($key, 0, 6) === 'extra.') {
  126. $bits = explode('.', $key);
  127. $last = array_pop($bits);
  128. $arr = &$config['extra'];
  129. foreach ($bits as $bit) {
  130. if (!isset($arr[$bit])) {
  131. $arr[$bit] = array();
  132. }
  133. $arr = &$arr[$bit];
  134. }
  135. $arr[$last] = $val;
  136. } else {
  137. $config[$key] = $val;
  138. }
  139. });
  140. }
  141. /**
  142. * {@inheritdoc}
  143. */
  144. public function removeProperty($name)
  145. {
  146. $authConfig = $this->authConfig;
  147. $this->manipulateJson('removeProperty', $name, function (&$config, $key) {
  148. if (substr($key, 0, 6) === 'extra.') {
  149. $bits = explode('.', $key);
  150. $last = array_pop($bits);
  151. $arr = &$config['extra'];
  152. foreach ($bits as $bit) {
  153. if (!isset($arr[$bit])) {
  154. return;
  155. }
  156. $arr = &$arr[$bit];
  157. }
  158. unset($arr[$last]);
  159. } else {
  160. unset($config[$key]);
  161. }
  162. });
  163. }
  164. /**
  165. * {@inheritdoc}
  166. */
  167. public function addLink($type, $name, $value)
  168. {
  169. $this->manipulateJson('addLink', $type, $name, $value, function (&$config, $type, $name, $value) {
  170. $config[$type][$name] = $value;
  171. });
  172. }
  173. /**
  174. * {@inheritdoc}
  175. */
  176. public function removeLink($type, $name)
  177. {
  178. $this->manipulateJson('removeSubNode', $type, $name, function (&$config, $type, $name) {
  179. unset($config[$type][$name]);
  180. });
  181. }
  182. protected function manipulateJson($method, $args, $fallback)
  183. {
  184. $args = func_get_args();
  185. // remove method & fallback
  186. array_shift($args);
  187. $fallback = array_pop($args);
  188. if ($this->file->exists()) {
  189. if (!is_writable($this->file->getPath())) {
  190. throw new \RuntimeException(sprintf('The file "%s" is not writable.', $this->file->getPath()));
  191. }
  192. if (!is_readable($this->file->getPath())) {
  193. throw new \RuntimeException(sprintf('The file "%s" is not readable.', $this->file->getPath()));
  194. }
  195. $contents = file_get_contents($this->file->getPath());
  196. } elseif ($this->authConfig) {
  197. $contents = "{\n}\n";
  198. } else {
  199. $contents = "{\n \"config\": {\n }\n}\n";
  200. }
  201. $manipulator = new JsonManipulator($contents);
  202. $newFile = !$this->file->exists();
  203. // override manipulator method for auth config files
  204. if ($this->authConfig && $method === 'addConfigSetting') {
  205. $method = 'addSubNode';
  206. list($mainNode, $name) = explode('.', $args[0], 2);
  207. $args = array($mainNode, $name, $args[1]);
  208. } elseif ($this->authConfig && $method === 'removeConfigSetting') {
  209. $method = 'removeSubNode';
  210. list($mainNode, $name) = explode('.', $args[0], 2);
  211. $args = array($mainNode, $name);
  212. }
  213. // try to update cleanly
  214. if (call_user_func_array(array($manipulator, $method), $args)) {
  215. file_put_contents($this->file->getPath(), $manipulator->getContents());
  216. } else {
  217. // on failed clean update, call the fallback and rewrite the whole file
  218. $config = $this->file->read();
  219. $this->arrayUnshiftRef($args, $config);
  220. call_user_func_array($fallback, $args);
  221. $this->file->write($config);
  222. }
  223. if ($newFile) {
  224. Silencer::call('chmod', $this->file->getPath(), 0600);
  225. }
  226. }
  227. /**
  228. * Prepend a reference to an element to the beginning of an array.
  229. *
  230. * @param array $array
  231. * @param mixed $value
  232. * @return array
  233. */
  234. private function arrayUnshiftRef(&$array, &$value)
  235. {
  236. $return = array_unshift($array, '');
  237. $array[0] = &$value;
  238. return $return;
  239. }
  240. }