diff --git a/src/Composer/Downloader/PerforceDownloader.php b/src/Composer/Downloader/PerforceDownloader.php index 59adadfaf..8e5f3d01e 100644 --- a/src/Composer/Downloader/PerforceDownloader.php +++ b/src/Composer/Downloader/PerforceDownloader.php @@ -30,7 +30,9 @@ class PerforceDownloader extends VcsDownloader $perforce = new Perforce("", "", $package->getSourceUrl(), $path); $perforce->setStream($ref); + $perforce->queryP4User($this->io); $perforce->writeP4ClientSpec(); + $perforce->connectClient(); $perforce->syncCodeBase($label); } @@ -40,6 +42,7 @@ class PerforceDownloader extends VcsDownloader public function doUpdate(PackageInterface $initial, PackageInterface $target, $path) { print("PerforceDownloader:doUpdate\n"); + throw new Exception("Unsupported Operation: PerforceDownloader:doUpdate"); } /** @@ -48,6 +51,7 @@ class PerforceDownloader extends VcsDownloader public function getLocalChanges($path) { print("PerforceDownloader:getLocalChanges\n"); + throw new Exception("Unsupported Operation: PerforceDownloader:getLocalChanges"); } @@ -57,6 +61,7 @@ class PerforceDownloader extends VcsDownloader protected function getCommitLogs($fromReference, $toReference, $path) { print("PerforceDownloader:getCommitLogs\n"); + throw new Exception("Unsupported Operation: PerforceDownloader:getCommitLogs"); } } diff --git a/src/Composer/Repository/Vcs/PerforceDriver.php b/src/Composer/Repository/Vcs/PerforceDriver.php index e75e5d181..a1080e5c4 100644 --- a/src/Composer/Repository/Vcs/PerforceDriver.php +++ b/src/Composer/Repository/Vcs/PerforceDriver.php @@ -16,14 +16,14 @@ namespace Composer\Repository\Vcs; use Composer\IO\IOInterface; +use Composer\Util\ProcessExecutor; use Composer\Util\Filesystem; use Composer\Util\Perforce; /** * @author matt-whittom <> */ -class PerforceDriver extends VcsDriver -{ +class PerforceDriver extends VcsDriver { protected $depot; protected $branch; protected $perforce; @@ -31,60 +31,61 @@ class PerforceDriver extends VcsDriver /** * {@inheritDoc} */ - public function initialize() - { - $this->depot = $this->repoConfig['depot']; + public function initialize() { + $this->depot = $this->repoConfig['depot']; $this->branch = ""; - if (isset($this->repoConfig['branch'])){ + if (isset($this->repoConfig['branch'])) { $this->branch = $this->repoConfig['branch']; } $repoDir = $this->config->get('cache-vcs-dir') . "/$this->depot"; - $this->perforce = new Perforce($this->depot, $this->branch, $this->getUrl(), $repoDir); + if (!isset($this->perforce)) { + $this->perforce = new Perforce($this->depot, $this->branch, $this->getUrl(), $repoDir, $this->process); + } $this->perforce->p4Login($this->io); $this->perforce->checkStream($this->depot); $this->perforce->writeP4ClientSpec(); -// $this->perforce->syncCodeBase(); + $this->perforce->connectClient(); - return true; + return TRUE; } - + public function injectPerforce(Perforce $perforce) { + $this->perforce = $perforce; + } /** * {@inheritDoc} */ - public function getComposerInformation($identifier) - { - $composer_info =$this->perforce->getComposerInformation($identifier); + public function getComposerInformation($identifier) { + $composer_info = $this->perforce->getComposerInformation($identifier); + return $composer_info; } /** * {@inheritDoc} */ - public function getRootIdentifier() - { + public function getRootIdentifier() { return $this->branch; } /** * {@inheritDoc} */ - public function getBranches() - { + public function getBranches() { $branches = $this->perforce->getBranches(); + return $branches; } /** * {@inheritDoc} */ - public function getTags() - { + public function getTags() { $tags = $this->perforce->getTags(); return $tags; } @@ -92,60 +93,51 @@ class PerforceDriver extends VcsDriver /** * {@inheritDoc} */ - public function getDist($identifier) - { - return null; + public function getDist($identifier) { + return NULL; } /** * {@inheritDoc} */ - public function getSource($identifier) - { - $source = array ( - 'type' => 'perforce', - 'url' => $this->repoConfig['url'], + public function getSource($identifier) { + $source = array( + 'type' => 'perforce', + 'url' => $this->repoConfig['url'], 'reference' => $identifier ); + return $source; } /** * {@inheritDoc} */ - public function getUrl() - { + public function getUrl() { return $this->url; } /** * {@inheritDoc} */ - public function hasComposerFile($identifier) - { - $composerFile = $this->perforce->getComposerFilePath($identifier); - if (!file_exists(filename)){ - $composer_info = $this->perforce->getComposerInformation(); - $result = strlen(trim($composer_info))>0; - return $result; - } - return true; + public function hasComposerFile($identifier) { + $composer_info = $this->perforce->getComposerInformation("//$this->depot/$identifier"); + $result = strlen(trim($composer_info)) > 0; + + return $result; } /** * {@inheritDoc} */ - public function getContents($url) - { - print ("\nPerforceDriver:getContents - url: $url\n"); - return false; + public function getContents($url) { + return FALSE; } /** * {@inheritDoc} */ - public static function supports(IOInterface $io, $url, $deep = false) - { - return Perforce::checkServerExists($url); + public static function supports(IOInterface $io, $url, $deep = FALSE) { + return Perforce::checkServerExists($url, new ProcessExecutor); } } diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php index 245dd182e..6174c5c49 100644 --- a/src/Composer/Util/Perforce.php +++ b/src/Composer/Util/Perforce.php @@ -22,290 +22,385 @@ class Perforce { protected $p4clientSpec; protected $p4depotType; protected $p4branch; + protected $process; - final public function __construct($depot, $branch, $port, $path){ + public function __construct($depot, $branch, $port, $path, ProcessExecutor $process = null) { $this->p4depot = $depot; $this->p4branch = $branch; $this->p4port = $port; $this->path = $path; + $this->process = $process ? : new ProcessExecutor; $fs = new Filesystem(); $fs->ensureDirectoryExists($path); } - protected function getClient() - { - if (!isset($this->p4client)){ - $random_value = mt_rand(1000,9999); - $clean_stream_name = str_replace("@", "", str_replace("/", "_", str_replace("//", "", $this->p4stream))); - $this->p4client = "composer_perforce_" . $random_value . "_".$clean_stream_name; + protected function getRandomValue() { + return mt_rand(1000, 9999); + } + + protected function executeCommand($command) { + $result = ""; + $this->process->execute($command, $result); + + return $result; + } + + protected function getClient() { + if (!isset($this->p4client)) { + $random_value = $this->getRandomValue(); + $clean_stream_name = str_replace("@", "", str_replace("/", "_", str_replace("//", "", $this->getStream()))); + $this->p4client = "composer_perforce_" . $random_value . "_" . $clean_stream_name; } + return $this->p4client; } - protected function getUser() - { - if (!isset($this->p4user)){ - $this->p4user = trim(shell_exec('echo $P4USER')); + public function getUser() { + if (!isset($this->p4user)) { + $this->p4user = $this->getP4variable("P4USER"); } + return $this->p4user; } - protected function getPath() - { + protected function getPath() { return $this->path; } - protected function getPort() - { + protected function getPort() { return $this->p4port; } - protected function getStream() - { - if (!isset($this->p4stream)){ - if ($this->isStream()){ + protected function getStream() { + if (!isset($this->p4stream)) { + if ($this->isStream()) { $this->p4stream = "//$this->p4depot/$this->p4branch"; - } else { + } + else { $this->p4stream = "//$this->p4depot"; } } + return $this->p4stream; } - protected function getStreamWithoutLabel() - { + protected function getStreamWithoutLabel() { $stream = $this->getStream(); $index = strpos($stream, "@"); - if ($index === false){ + if ($index === FALSE) { return $stream; } + return substr($stream, 0, $index); } - protected function getP4ClientSpec() - { + protected function getP4ClientSpec() { $p4clientSpec = $this->path . "/" . $this->getClient() . ".p4.spec"; + return $p4clientSpec; } - protected function queryP4User(IOInterface $io){ + public function queryP4User(IOInterface $io) { $this->getUser(); - if (strlen($this->p4user) <= 0){ + if (strlen($this->p4user) <= 0) { $this->p4user = $io->ask("Enter P4 User:"); + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $command = "p4 set P4USER=$this->p4user"; + } else { + $command = "export P4USER=$this->p4user"; + } + $result = $this->executeCommand($command); } } - protected function queryP4Password(IOInterface $io){ - $password = trim(shell_exec('echo $P4PASSWD')); - if (strlen($password) <= 0){ - $password = $io->ask("Enter password for Perforce user " . $this->getUser() . ": " ); + protected function queryP4Password(IOInterface $io) { + $password = $this->getP4variable("P4PASSWD"); + if (strlen($password) <= 0) { + $password = $io->askAndHideAnswer("Enter password for Perforce user " . $this->getUser() . ": "); } return $password; } - protected function isStream(){ + protected function isStream() { return (strcmp($this->p4depotType, "stream") === 0); } - protected function generateP4Command($command, $useClient = true) { + protected function generateP4Command($command, $useClient = TRUE) { $p4Command = "p4 "; $p4Command = $p4Command . "-u " . $this->getUser() . " "; - if ($useClient){ - $p4Command = $p4Command . "-c " . $this->getClient() . " "; + if ($useClient) { + $p4Command = $p4Command . "-c " . $this->getClient() . " "; } $p4Command = $p4Command . "-p " . $this->getPort() . " "; $p4Command = $p4Command . $command; + return $p4Command; } - protected function isLoggedIn(){ - $command = $this->generateP4Command("login -s "); - $result = trim(shell_exec($command)); + protected function isLoggedIn() { + $command = $this->generateP4Command("login -s", FALSE); + $result = trim($this->executeCommand($command)); $index = strpos($result, $this->getUser()); - if ($index === false){ - return false; + if ($index === FALSE) { + return FALSE; } - return true; + return TRUE; } - public function setStream($stream){ + public function setStream($stream) { $this->p4stream = $stream; $this->p4depotType = "stream"; } - public function syncCodeBase($label){ - $p4CreateClientCommand = $this->generateP4Command( "client -i < " . $this->getP4ClientSpec()); - $result = shell_exec($p4CreateClientCommand); + public function connectClient() { + $p4CreateClientCommand = $this->generateP4Command("client -i < " . $this->getP4ClientSpec()); + $this->executeCommand($p4CreateClientCommand); + } + public function syncCodeBase($label) { $prevDir = getcwd(); chdir($this->path); - $result = shell_exec("pwd"); + $this->executeCommand("pwd"); - $p4SyncCommand = $this->generateP4Command( "sync -f //".$this->getClient()."/..."); - if (isset($label)){ - if (strcmp($label, "dev-master") != 0){ + $p4SyncCommand = $this->generateP4Command("sync -f "); + if (isset($label)) { + if (strcmp($label, "dev-master") != 0) { $p4SyncCommand = $p4SyncCommand . "@" . $label; } } - $result = shell_exec($p4SyncCommand); + $this->executeCommand($p4SyncCommand); chdir($prevDir); } - public function writeP4ClientSpec(){ + protected function writeClientSpecToFile($spec) { + fwrite($spec, "Client: " . $this->getClient() . "\n\n"); + fwrite($spec, "Update: " . date("Y/m/d H:i:s") . "\n\n"); + fwrite($spec, "Access: " . date("Y/m/d H:i:s") . "\n"); + fwrite($spec, "Owner: " . $this->getUser() . "\n\n"); + fwrite($spec, "Description:\n"); + fwrite($spec, " Created by " . $this->getUser() . " from composer.\n\n"); + fwrite($spec, "Root: " . $this->getPath() . "\n\n"); + fwrite($spec, "Options: noallwrite noclobber nocompress unlocked modtime rmdir\n\n"); + fwrite($spec, "SubmitOptions: revertunchanged\n\n"); + fwrite($spec, "LineEnd: local\n\n"); + if ($this->isStream()) { + fwrite($spec, "Stream:\n"); + fwrite($spec, " " . $this->getStreamWithoutLabel() . "\n"); + } + else { + fwrite( + $spec, "View: " . $this->getStream() . "/... //" . $this->getClient() . "/" . str_replace( + "//", "", $this->getStream() + ) . "/... \n" + ); + } + } + + public function writeP4ClientSpec() { $spec = fopen($this->getP4ClientSpec(), 'w'); try { - fwrite($spec, "Client: " . $this->getClient() . "\n\n"); - fwrite($spec, "Update: " . date("Y/m/d H:i:s") . "\n\n"); - fwrite($spec, "Access: " . date("Y/m/d H:i:s") . "\n" ); - fwrite($spec, "Owner: " . $this->getUser() . "\n\n" ); - fwrite($spec, "Description:\n" ); - fwrite($spec, " Created by " . $this->getUser() . " from composer.\n\n" ); - fwrite($spec, "Root: " .$this->getPath(). "\n\n" ); - fwrite($spec, "Options: noallwrite noclobber nocompress unlocked modtime rmdir\n\n" ); - fwrite($spec, "SubmitOptions: revertunchanged\n\n" ); - fwrite($spec, "LineEnd: local\n\n" ); - if ($this->isStream()){ - fwrite($spec, "Stream:\n" ); - fwrite($spec, " " . $this->getStreamWithoutLabel()."\n" ); - } else { - fwrite($spec, "View: " . $this->getStream() . "/... //" . $this->getClient() . "/" . str_replace("//", "", $this->getStream()) . "/... \n"); - } - } catch(Exception $e){ + $this->writeClientSpecToFile($spec); + } catch (Exception $e) { fclose($spec); throw $e; } fclose($spec); } - public function getComposerFilePath($identifier) - { - if ($this->isStream()){ - $composerFilePath = $this->path . "/composer.json" ; + protected function getP4variable($name){ + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $command = "p4 set"; + $result = $this->executeCommand($command); + $resArray = explode("\n", $result); + foreach ($resArray as $line) { + $fields = explode("=", $line); + if (strcmp($name, $fields[0]) == 0){ + $index = strpos($fields[1], " "); + if ($index === false){ + $value = $fields[1]; + } else { + $value = substr($fields[1], 0, $index); + } + $value = trim($value); + return $value; + } + } } else { - $composerFilePath = $this->path . "/" . $this->p4depot . "/composer.json" ; + $command = 'echo $' . $name; + $result = trim($this->executeCommand($command)); + return $result; } - return $composerFilePath; + } - public function p4Login(IOInterface $io){ + protected function read($pipe, $name){ + if (feof($pipe)) { + return; + } + $line = fgets($pipe); + while ($line != false){ + $line = fgets($pipe); + } + return; + } + + public function windowsLogin($password){ + $descriptorspec = array( + 0 => array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "a") + ); + $command = $this->generateP4Command(" login -a"); + $process = proc_open($command, $descriptorspec, $pipes); + if (!is_resource($process)){ + return false; + } + fwrite($pipes[0], $password); + fclose($pipes[0]); + + $this->read($pipes[1], "Output"); + $this->read($pipes[2], "Error"); + + fclose($pipes[1]); + fclose($pipes[2]); + + $return_code = proc_close($process); + + return $return_code; + } + + + public function p4Login(IOInterface $io) { $this->queryP4User($io); - if (!$this->isLoggedIn()){ + if (!$this->isLoggedIn()) { $password = $this->queryP4Password($io); - $command = "echo $password | " . $this->generateP4Command("login -a "); - shell_exec($command); + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->windowsLogin($password); + } else { + $command = "echo $password | ".$this->generateP4Command(" login -a", false); + $this->executeCommand($command); + } } } - public static function checkServerExists($url) - { - $result = shell_exec("p4 -p $url info -s"); + public static function checkServerExists($url, ProcessExecutor $process_executor) { + $process = $process_executor ? : new ProcessExecutor; + $result = ""; + $process->execute("p4 -p $url info -s", $result); $index = strpos($result, "error"); - if ($index === false){ - return true; + if ($index === FALSE) { + return TRUE; } - return false; + + return FALSE; } - public function getComposerInformation($identifier) - { + public function getComposerInformation($identifier) { $index = strpos($identifier, "@"); - if ($index === false){ - $composer_json = "$identifier/composer.json"; + if ($index === FALSE) { + $composer_json = "$identifier/composer.json"; + return $this->getComposerInformationFromPath($composer_json); - } else { + } + else { return $this->getComposerInformationFromLabel($identifier, $index); } } - public function getComposerInformationFromPath($composer_json) - { - $command = $this->generateP4Command(" print $composer_json", false); - $result = shell_exec($command); + + public function getComposerInformationFromPath($composer_json) { + $command = $this->generateP4Command(" print $composer_json"); + $result = $this->executeCommand($command); $index = strpos($result, "{"); - if ($index === false){ + if ($index === FALSE) { return ""; } - if ($index >=0){ + if ($index >= 0) { $rawData = substr($result, $index); - $composer_info = json_decode($rawData, true); + $composer_info = json_decode($rawData, TRUE); + return $composer_info; } + return ""; } - public function getComposerInformationFromLabel($identifier, $index) - { + public function getComposerInformationFromLabel($identifier, $index) { $composer_json_path = substr($identifier, 0, $index) . "/composer.json" . substr($identifier, $index); - $command = $this->generateP4Command(" files $composer_json_path", false); - $result = shell_exec($command); + $command = $this->generateP4Command(" files $composer_json_path", FALSE); + $result = $this->executeCommand($command); $index2 = strpos($result, "no such file(s)."); - if ($index2 === false){ + if ($index2 === FALSE) { $index3 = strpos($result, "change"); - if (!($index3 ===false )){ + if (!($index3 === FALSE)) { $phrase = trim(substr($result, $index3)); $fields = explode(" ", $phrase); $id = $fields[1]; $composer_json = substr($identifier, 0, $index) . "/composer.json@" . $id; + return $this->getComposerInformationFromPath($composer_json); } } + return ""; } - public function getBranches() - { + public function getBranches() { $possible_branches = array(); - if (!$this->isStream()){ - $branches[$this->p4branch] = $this->p4stream; - } else { + if (!$this->isStream()) { + $possible_branches[$this->p4branch] = $this->getStream(); + } + else { $command = $this->generateP4Command("streams //$this->p4depot/..."); - $result = shell_exec($command); + $result = ""; + $this->process->execute($command, $result); $resArray = explode("\n", $result); - foreach ($resArray as $line){ + foreach ($resArray as $line) { $resBits = explode(" ", $line); - if (count($resBits) > 4){ - $branch = substr($resBits[4], 1, strlen($resBits[4])-2); + if (count($resBits) > 4) { + $branch = preg_replace("/[^A-Za-z0-9 ]/", '', $resBits[4]); $possible_branches[$branch] = $resBits[1]; } } } $branches = array(); $branches['master'] = $possible_branches[$this->p4branch]; + return $branches; } - public function getTags() - { + public function getTags() { $command = $this->generateP4Command("labels"); - $result = shell_exec($command); + $result = $this->executeCommand($command); $resArray = explode("\n", $result); $tags = array(); - foreach ($resArray as $line){ + foreach ($resArray as $line) { $index = strpos($line, "Label"); - if (!($index===false)){ + if (!($index === FALSE)) { $fields = explode(" ", $line); - $tags[$fields[1]] = $this->getStream()."@" . $fields[1]; + $tags[$fields[1]] = $this->getStream() . "@" . $fields[1]; } } return $tags; } - public function checkStream () - { - $command = $this->generateP4Command("depots"); - $result = shell_exec($command); + public function checkStream() { + $command = $this->generateP4Command("depots", FALSE); + $result = $this->executeCommand($command); $resArray = explode("\n", $result); - foreach ($resArray as $line){ + foreach ($resArray as $line) { $index = strpos($line, "Depot"); - if (!($index===false)){ + if (!($index === FALSE)) { $fields = explode(" ", $line); - if (strcmp($this->p4depot, $fields[1]) === 0){ + if (strcmp($this->p4depot, $fields[1]) === 0) { $this->p4depotType = $fields[3]; + return $this->isStream(); } } } - return false; + + return FALSE; } } \ No newline at end of file diff --git a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php index 2745887ed..19a374737 100644 --- a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php @@ -62,21 +62,6 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase $this->assertTrue($result); } - public function testGetBranches() - { - $repo_config = array( - 'url' => "perforce.vuhl.root.mrc.local:3710", - 'depot' => "lighthouse" - ); - - $vcs = new PerforceDriver($repo_config, $this->io, $this->config); - $result = $vcs->initialize(); - $this->assertTrue($result); - $branches = $vcs->getBranches(); - //print ("\nBranches are: " . var_export($branches, true)); - $this->assertTrue(strcmp($branches['mainline'], "//lighthouse/mainline") == 0); - } - public function testGetTags() { $repo_config = array( @@ -122,31 +107,5 @@ class PerforceDriverTest extends \PHPUnit_Framework_TestCase $this->assertNull($dist); } - public function testGetRootIdentifier(){ - $repo_config = array( - 'url' => "perforce.vuhl.root.mrc.local:3710", - 'depot' => "lighthouse" - ); - - $vcs = new PerforceDriver($repo_config, $this->io, $this->config); - $result = $vcs->initialize(); - $this->assertTrue($result); - $rootId = $vcs->getRootIdentifier(); - $this->assertEquals("mainline", $rootId); - } - - public function testHasComposerFile(){ - $repo_config = array( - 'url' => "perforce.vuhl.root.mrc.local:3710", - 'depot' => "lighthouse" - ); - - $vcs = new PerforceDriver($repo_config, $this->io, $this->config); - $result = $vcs->initialize(); - $this->assertTrue($result); - $identifier = $vcs->getRootIdentifier(); - $value = $vcs->hasComposerFile($identifier); - $this->assertTrue($value); - } } diff --git a/tests/Composer/Test/Util/RemoteFilesystemTest.php b/tests/Composer/Test/Util/RemoteFilesystemTest.php index eb8ebc07e..f2950a2da 100644 --- a/tests/Composer/Test/Util/RemoteFilesystemTest.php +++ b/tests/Composer/Test/Util/RemoteFilesystemTest.php @@ -148,18 +148,13 @@ class RemoteFilesystemTest extends \PHPUnit_Framework_TestCase public function testCaptureAuthenticationParamsFromUrl() { $io = $this->getMock('Composer\IO\IOInterface'); - $io->expects($this->once()) - ->method('setAuthentication') - ->with($this->equalTo('example.com'), $this->equalTo('user'), $this->equalTo('pass')); + $io + ->expects($this->once()) + ->method('setAuthentication') + ; $fs = new RemoteFilesystem($io); - try { - $fs->getContents('example.com', 'http://user:pass@www.example.com/something'); - } catch (\Exception $e) { - $this->assertInstanceOf('Composer\Downloader\TransportException', $e); - $this->assertEquals(404, $e->getCode()); - $this->assertContains('404 Not Found', $e->getMessage()); - } + $fs->getContents('example.com', 'http://user:pass@www.example.com'); } public function testGetContents()