JsonConfigSource.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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. /**
  15. * JSON Configuration Source
  16. *
  17. * @author Jordi Boggiano <j.boggiano@seld.be>
  18. * @author Beau Simensen <beau@dflydev.com>
  19. */
  20. class JsonConfigSource implements ConfigSourceInterface
  21. {
  22. /**
  23. * @var \Composer\Json\JsonFile
  24. */
  25. private $file;
  26. /**
  27. * @var bool
  28. */
  29. private $authConfig;
  30. /**
  31. * Constructor
  32. *
  33. * @param JsonFile $file
  34. */
  35. public function __construct(JsonFile $file, $authConfig = false)
  36. {
  37. $this->file = $file;
  38. $this->authConfig = $authConfig;
  39. }
  40. /**
  41. * {@inheritdoc}
  42. */
  43. public function getName()
  44. {
  45. return $this->file->getPath();
  46. }
  47. /**
  48. * {@inheritdoc}
  49. */
  50. public function addRepository($name, $config)
  51. {
  52. $this->manipulateJson('addRepository', $name, $config, function (&$config, $repo, $repoConfig) {
  53. $config['repositories'][$repo] = $repoConfig;
  54. });
  55. }
  56. /**
  57. * {@inheritdoc}
  58. */
  59. public function removeRepository($name)
  60. {
  61. $this->manipulateJson('removeRepository', $name, function (&$config, $repo) {
  62. unset($config['repositories'][$repo]);
  63. });
  64. }
  65. /**
  66. * {@inheritdoc}
  67. */
  68. public function addConfigSetting($name, $value)
  69. {
  70. $this->manipulateJson('addConfigSetting', $name, $value, function (&$config, $key, $val) {
  71. if ($key === 'github-oauth' || $key === 'http-basic') {
  72. list($key, $host) = explode('.', $key, 2);
  73. if ($this->authConfig) {
  74. $config[$key][$host] = $val;
  75. } else {
  76. $config['config'][$key][$host] = $val;
  77. }
  78. } else {
  79. $config['config'][$key] = $val;
  80. }
  81. });
  82. }
  83. /**
  84. * {@inheritdoc}
  85. */
  86. public function removeConfigSetting($name)
  87. {
  88. $this->manipulateJson('removeConfigSetting', $name, function (&$config, $key) {
  89. if ($key === 'github-oauth' || $key === 'http-basic') {
  90. list($key, $host) = explode('.', $key, 2);
  91. if ($this->authConfig) {
  92. unset($config[$key][$host]);
  93. } else {
  94. unset($config['config'][$key][$host]);
  95. }
  96. } else {
  97. unset($config['config'][$key]);
  98. }
  99. });
  100. }
  101. /**
  102. * {@inheritdoc}
  103. */
  104. public function addLink($type, $name, $value)
  105. {
  106. $this->manipulateJson('addLink', $type, $name, $value, function (&$config, $type, $name, $value) {
  107. $config[$type][$name] = $value;
  108. });
  109. }
  110. /**
  111. * {@inheritdoc}
  112. */
  113. public function removeLink($type, $name)
  114. {
  115. $this->manipulateJson('removeSubNode', $type, $name, function (&$config, $type, $name) {
  116. unset($config[$type][$name]);
  117. });
  118. }
  119. protected function manipulateJson($method, $args, $fallback)
  120. {
  121. $args = func_get_args();
  122. // remove method & fallback
  123. array_shift($args);
  124. $fallback = array_pop($args);
  125. if ($this->file->exists()) {
  126. $contents = file_get_contents($this->file->getPath());
  127. } elseif ($this->authConfig) {
  128. $contents = "{\n}\n";
  129. } else {
  130. $contents = "{\n \"config\": {\n }\n}\n";
  131. }
  132. $manipulator = new JsonManipulator($contents);
  133. $newFile = !$this->file->exists();
  134. // override manipulator method for auth config files
  135. if ($this->authConfig && $method === 'addConfigSetting') {
  136. $method = 'addSubNode';
  137. list($mainNode, $name) = explode('.', $args[0], 2);
  138. $args = array($mainNode, $name, $args[1]);
  139. } elseif ($this->authConfig && $method === 'removeConfigSetting') {
  140. $method = 'removeSubNode';
  141. list($mainNode, $name) = explode('.', $args[0], 2);
  142. $args = array($mainNode, $name);
  143. }
  144. // try to update cleanly
  145. if (call_user_func_array(array($manipulator, $method), $args)) {
  146. file_put_contents($this->file->getPath(), $manipulator->getContents());
  147. } else {
  148. // on failed clean update, call the fallback and rewrite the whole file
  149. $config = $this->file->read();
  150. $this->arrayUnshiftRef($args, $config);
  151. call_user_func_array($fallback, $args);
  152. $this->file->write($config);
  153. }
  154. if ($newFile) {
  155. @chmod($this->file->getPath(), 0600);
  156. }
  157. }
  158. /**
  159. * Prepend a reference to an element to the beginning of an array.
  160. *
  161. * @param array $array
  162. * @param mixed $value
  163. * @return array
  164. */
  165. private function arrayUnshiftRef(&$array, &$value)
  166. {
  167. $return = array_unshift($array, '');
  168. $array[0] =& $value;
  169. return $return;
  170. }
  171. }