Perforce.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. <?php
  2. /**
  3. * Created by JetBrains PhpStorm.
  4. * User: matt.whittom
  5. * Date: 7/23/13
  6. * Time: 3:22 PM
  7. * To change this template use File | Settings | File Templates.
  8. */
  9. namespace Composer\Util;
  10. use Composer\IO\IOInterface;
  11. class Perforce {
  12. protected $path;
  13. protected $p4client;
  14. protected $p4user;
  15. protected $p4password;
  16. protected $p4port;
  17. protected $p4stream;
  18. protected $p4clientSpec;
  19. protected $p4depotType;
  20. protected $p4branch;
  21. protected $process;
  22. public function __construct($repoConfig, $port, $path, ProcessExecutor $process = NULL) {
  23. $this->p4port = $port;
  24. $this->path = $path;
  25. $this->process = $process ? : new ProcessExecutor;
  26. $fs = new Filesystem();
  27. $fs->ensureDirectoryExists($path);
  28. if (isset($repoConfig['depot'])) {
  29. $this->p4depot = $repoConfig['depot'];
  30. }
  31. if (isset($repoConfig['branch'])) {
  32. $this->p4branch = $repoConfig['branch'];
  33. }
  34. if (isset($repoConfig['p4user'])) {
  35. $this->p4user = $repoConfig['p4user'];
  36. }
  37. else {
  38. $this->p4user = $this->getP4variable("P4USER");
  39. }
  40. if (isset($repoConfig['p4password'])) {
  41. $this->p4password = $repoConfig['p4password'];
  42. }
  43. }
  44. protected function getRandomValue() {
  45. return mt_rand(1000, 9999);
  46. }
  47. protected function isWindows(){
  48. return defined('PHP_WINDOWS_VERSION_BUILD');
  49. }
  50. protected function executeCommand($command) {
  51. $result = "";
  52. $this->process->execute($command, $result);
  53. return $result;
  54. }
  55. protected function getClient() {
  56. if (!isset($this->p4client)) {
  57. $random_value = $this->getRandomValue();
  58. $clean_stream_name = str_replace("@", "", str_replace("/", "_", str_replace("//", "", $this->getStream())));
  59. $this->p4client = "composer_perforce_" . $random_value . "_" . $clean_stream_name;
  60. }
  61. return $this->p4client;
  62. }
  63. protected function getPath() {
  64. return $this->path;
  65. }
  66. protected function getPort() {
  67. return $this->p4port;
  68. }
  69. protected function isStream() {
  70. return (strcmp($this->p4depotType, "stream") === 0);
  71. }
  72. protected function getStream() {
  73. if (!isset($this->p4stream)) {
  74. if ($this->isStream()) {
  75. $this->p4stream = "//$this->p4depot/$this->p4branch";
  76. }
  77. else {
  78. $this->p4stream = "//$this->p4depot";
  79. }
  80. }
  81. return $this->p4stream;
  82. }
  83. protected function getStreamWithoutLabel() {
  84. $stream = $this->getStream();
  85. $index = strpos($stream, "@");
  86. if ($index === FALSE) {
  87. return $stream;
  88. }
  89. return substr($stream, 0, $index);
  90. }
  91. protected function getP4ClientSpec() {
  92. $p4clientSpec = $this->path . "/" . $this->getClient() . ".p4.spec";
  93. return $p4clientSpec;
  94. }
  95. public function getUser() {
  96. return $this->p4user;
  97. }
  98. public function queryP4User(IOInterface $io) {
  99. $this->getUser();
  100. if (strlen($this->p4user) > 0) {
  101. return;
  102. }
  103. $this->p4user = $this->getP4variable("P4USER");
  104. if (strlen($this->p4user) > 0) {
  105. return;
  106. }
  107. $this->p4user = $io->ask("Enter P4 User:");
  108. if ($this->isWindows()) {
  109. $command = "p4 set P4USER=$this->p4user";
  110. }
  111. else {
  112. $command = "export P4USER=$this->p4user";
  113. }
  114. $result = $this->executeCommand($command);
  115. }
  116. protected function getP4variable($name) {
  117. if ($this->isWindows()) {
  118. $command = "p4 set";
  119. $result = $this->executeCommand($command);
  120. $resArray = explode("\n", $result);
  121. foreach ($resArray as $line) {
  122. $fields = explode("=", $line);
  123. if (strcmp($name, $fields[0]) == 0) {
  124. $index = strpos($fields[1], " ");
  125. if ($index === FALSE) {
  126. $value = $fields[1];
  127. }
  128. else {
  129. $value = substr($fields[1], 0, $index);
  130. }
  131. $value = trim($value);
  132. return $value;
  133. }
  134. }
  135. }
  136. else {
  137. $command = 'echo $' . $name;
  138. $result = trim($this->executeCommand($command));
  139. return $result;
  140. }
  141. }
  142. protected function queryP4Password(IOInterface $io) {
  143. if (isset($this->p4password)) {
  144. return $this->p4password;
  145. }
  146. $password = $this->getP4variable("P4PASSWD");
  147. if (strlen($password) <= 0) {
  148. $password = $io->askAndHideAnswer("Enter password for Perforce user " . $this->getUser() . ": ");
  149. }
  150. $this->p4password = $password;
  151. return $password;
  152. }
  153. protected function generateP4Command($command, $useClient = TRUE) {
  154. $p4Command = "p4 ";
  155. $p4Command = $p4Command . "-u " . $this->getUser() . " ";
  156. if ($useClient) {
  157. $p4Command = $p4Command . "-c " . $this->getClient() . " ";
  158. }
  159. $p4Command = $p4Command . "-p " . $this->getPort() . " ";
  160. $p4Command = $p4Command . $command;
  161. return $p4Command;
  162. }
  163. protected function isLoggedIn() {
  164. $command = $this->generateP4Command("login -s", FALSE);
  165. $result = trim($this->executeCommand($command));
  166. $index = strpos($result, $this->getUser());
  167. if ($index === FALSE) {
  168. return FALSE;
  169. }
  170. return TRUE;
  171. }
  172. public function setStream($stream) {
  173. $this->p4stream = $stream;
  174. $this->p4depotType = "stream";
  175. }
  176. public function connectClient() {
  177. $p4CreateClientCommand = $this->generateP4Command("client -i < " . $this->getP4ClientSpec());
  178. $this->executeCommand($p4CreateClientCommand);
  179. }
  180. public function syncCodeBase($label) {
  181. $prevDir = getcwd();
  182. chdir($this->path);
  183. $this->executeCommand("pwd");
  184. $p4SyncCommand = $this->generateP4Command("sync -f ");
  185. if (isset($label)) {
  186. if (strcmp($label, "dev-master") != 0) {
  187. $p4SyncCommand = $p4SyncCommand . "@" . $label;
  188. }
  189. }
  190. $this->executeCommand($p4SyncCommand);
  191. chdir($prevDir);
  192. }
  193. protected function writeClientSpecToFile($spec) {
  194. fwrite($spec, "Client: " . $this->getClient() . "\n\n");
  195. fwrite($spec, "Update: " . date("Y/m/d H:i:s") . "\n\n");
  196. fwrite($spec, "Access: " . date("Y/m/d H:i:s") . "\n");
  197. fwrite($spec, "Owner: " . $this->getUser() . "\n\n");
  198. fwrite($spec, "Description:\n");
  199. fwrite($spec, " Created by " . $this->getUser() . " from composer.\n\n");
  200. fwrite($spec, "Root: " . $this->getPath() . "\n\n");
  201. fwrite($spec, "Options: noallwrite noclobber nocompress unlocked modtime rmdir\n\n");
  202. fwrite($spec, "SubmitOptions: revertunchanged\n\n");
  203. fwrite($spec, "LineEnd: local\n\n");
  204. if ($this->isStream()) {
  205. fwrite($spec, "Stream:\n");
  206. fwrite($spec, " " . $this->getStreamWithoutLabel() . "\n");
  207. }
  208. else {
  209. fwrite(
  210. $spec, "View: " . $this->getStream() . "/... //" . $this->getClient() . "/" . str_replace(
  211. "//", "", $this->getStream()
  212. ) . "/... \n"
  213. );
  214. }
  215. }
  216. public function writeP4ClientSpec() {
  217. $spec = fopen($this->getP4ClientSpec(), 'w');
  218. try {
  219. $this->writeClientSpecToFile($spec);
  220. } catch (Exception $e) {
  221. fclose($spec);
  222. throw $e;
  223. }
  224. fclose($spec);
  225. }
  226. protected function read($pipe, $name) {
  227. if (feof($pipe)) {
  228. return;
  229. }
  230. $line = fgets($pipe);
  231. while ($line != FALSE) {
  232. $line = fgets($pipe);
  233. }
  234. return;
  235. }
  236. public function windowsLogin($password) {
  237. $descriptorspec = array(
  238. 0 => array("pipe", "r"),
  239. 1 => array("pipe", "w"),
  240. 2 => array("pipe", "a")
  241. );
  242. $command = $this->generateP4Command(" login -a");
  243. $process = proc_open($command, $descriptorspec, $pipes);
  244. if (!is_resource($process)) {
  245. return FALSE;
  246. }
  247. fwrite($pipes[0], $password);
  248. fclose($pipes[0]);
  249. $this->read($pipes[1], "Output");
  250. $this->read($pipes[2], "Error");
  251. fclose($pipes[1]);
  252. fclose($pipes[2]);
  253. $return_code = proc_close($process);
  254. return $return_code;
  255. }
  256. public function p4Login(IOInterface $io) {
  257. $this->queryP4User($io);
  258. if (!$this->isLoggedIn()) {
  259. $password = $this->queryP4Password($io);
  260. if ($this->isWindows()) {
  261. $this->windowsLogin($password);
  262. }
  263. else {
  264. $command = "echo $password | " . $this->generateP4Command(" login -a", FALSE);
  265. $this->executeCommand($command);
  266. }
  267. }
  268. }
  269. public static function checkServerExists($url, ProcessExecutor $process_executor) {
  270. $process = $process_executor ? : new ProcessExecutor;
  271. $result = "";
  272. $process->execute("p4 -p $url info -s", $result);
  273. $index = strpos($result, "error");
  274. if ($index === FALSE) {
  275. return TRUE;
  276. }
  277. return FALSE;
  278. }
  279. public function getComposerInformation($identifier) {
  280. $index = strpos($identifier, "@");
  281. if ($index === FALSE) {
  282. $composer_json = "$identifier/composer.json";
  283. return $this->getComposerInformationFromPath($composer_json);
  284. }
  285. else {
  286. return $this->getComposerInformationFromLabel($identifier, $index);
  287. }
  288. }
  289. public function getComposerInformationFromPath($composer_json) {
  290. $command = $this->generateP4Command(" print $composer_json");
  291. $result = $this->executeCommand($command);
  292. $index = strpos($result, "{");
  293. if ($index === FALSE) {
  294. return "";
  295. }
  296. if ($index >= 0) {
  297. $rawData = substr($result, $index);
  298. $composer_info = json_decode($rawData, TRUE);
  299. return $composer_info;
  300. }
  301. return "";
  302. }
  303. public function getComposerInformationFromLabel($identifier, $index) {
  304. $composer_json_path = substr($identifier, 0, $index) . "/composer.json" . substr($identifier, $index);
  305. $command = $this->generateP4Command(" files $composer_json_path", FALSE);
  306. $result = $this->executeCommand($command);
  307. $index2 = strpos($result, "no such file(s).");
  308. if ($index2 === FALSE) {
  309. $index3 = strpos($result, "change");
  310. if (!($index3 === FALSE)) {
  311. $phrase = trim(substr($result, $index3));
  312. $fields = explode(" ", $phrase);
  313. $id = $fields[1];
  314. $composer_json = substr($identifier, 0, $index) . "/composer.json@" . $id;
  315. return $this->getComposerInformationFromPath($composer_json);
  316. }
  317. }
  318. return "";
  319. }
  320. public function getBranches() {
  321. $possible_branches = array();
  322. if (!$this->isStream()) {
  323. $possible_branches[$this->p4branch] = $this->getStream();
  324. }
  325. else {
  326. $command = $this->generateP4Command("streams //$this->p4depot/...");
  327. $result = "";
  328. $this->process->execute($command, $result);
  329. $resArray = explode("\n", $result);
  330. foreach ($resArray as $line) {
  331. $resBits = explode(" ", $line);
  332. if (count($resBits) > 4) {
  333. $branch = preg_replace("/[^A-Za-z0-9 ]/", '', $resBits[4]);
  334. $possible_branches[$branch] = $resBits[1];
  335. }
  336. }
  337. }
  338. $branches = array();
  339. $branches['master'] = $possible_branches[$this->p4branch];
  340. return $branches;
  341. }
  342. public function getTags() {
  343. $command = $this->generateP4Command("labels");
  344. $result = $this->executeCommand($command);
  345. $resArray = explode("\n", $result);
  346. $tags = array();
  347. foreach ($resArray as $line) {
  348. $index = strpos($line, "Label");
  349. if (!($index === FALSE)) {
  350. $fields = explode(" ", $line);
  351. $tags[$fields[1]] = $this->getStream() . "@" . $fields[1];
  352. }
  353. }
  354. return $tags;
  355. }
  356. public function checkStream() {
  357. $command = $this->generateP4Command("depots", FALSE);
  358. $result = $this->executeCommand($command);
  359. $resArray = explode("\n", $result);
  360. foreach ($resArray as $line) {
  361. $index = strpos($line, "Depot");
  362. if (!($index === FALSE)) {
  363. $fields = explode(" ", $line);
  364. if (strcmp($this->p4depot, $fields[1]) === 0) {
  365. $this->p4depotType = $fields[3];
  366. return $this->isStream();
  367. }
  368. }
  369. }
  370. return FALSE;
  371. }
  372. }