JsonConfigSource.php 5.2 KB

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