class Perforce
@author Matt Whittom <Matt.Whittom@veteransunited.com>
@phpstan-type RepoConfig array{unique_perforce_client_name?: string, depot?: string, branch?: string, p4user?: string, p4password?: string}
Hierarchy
- class \Composer\Util\Perforce
Expanded class hierarchy of Perforce
2 files declare their use of Perforce
- PerforceDownloader.php in vendor/
composer/ composer/ src/ Composer/ Downloader/ PerforceDownloader.php - PerforceDriver.php in vendor/
composer/ composer/ src/ Composer/ Repository/ Vcs/ PerforceDriver.php
3 string references to 'Perforce'
- Factory::createDownloadManager in vendor/
composer/ composer/ src/ Composer/ Factory.php - PerforceDriver::getSource in vendor/
composer/ composer/ src/ Composer/ Repository/ Vcs/ PerforceDriver.php - @inheritDoc
- RepositoryFactory::manager in vendor/
composer/ composer/ src/ Composer/ Repository/ RepositoryFactory.php
File
-
vendor/
composer/ composer/ src/ Composer/ Util/ Perforce.php, line 25
Namespace
Composer\UtilView source
class Perforce {
/** @var string */
protected $path;
/** @var ?string */
protected $p4Depot;
/** @var ?string */
protected $p4Client;
/** @var ?string */
protected $p4User;
/** @var ?string */
protected $p4Password;
/** @var string */
protected $p4Port;
/** @var ?string */
protected $p4Stream;
/** @var string */
protected $p4ClientSpec;
/** @var ?string */
protected $p4DepotType;
/** @var ?string */
protected $p4Branch;
/** @var ProcessExecutor */
protected $process;
/** @var string */
protected $uniquePerforceClientName;
/** @var bool */
protected $windowsFlag;
/** @var string */
protected $commandResult;
/** @var IOInterface */
protected $io;
/** @var ?Filesystem */
protected $filesystem;
/**
* @phpstan-param RepoConfig $repoConfig
*/
public function __construct($repoConfig, string $port, string $path, ProcessExecutor $process, bool $isWindows, IOInterface $io) {
$this->windowsFlag = $isWindows;
$this->p4Port = $port;
$this->initializePath($path);
$this->process = $process;
$this->initialize($repoConfig);
$this->io = $io;
}
/**
* @phpstan-param RepoConfig $repoConfig
*/
public static function create($repoConfig, string $port, string $path, ProcessExecutor $process, IOInterface $io) : self {
return new Perforce($repoConfig, $port, $path, $process, Platform::isWindows(), $io);
}
public static function checkServerExists(string $url, ProcessExecutor $processExecutor) : bool {
return 0 === $processExecutor->execute([
'p4',
'-p',
$url,
'info',
'-s',
], $ignoredOutput);
}
/**
* @phpstan-param RepoConfig $repoConfig
*/
public function initialize($repoConfig) : void {
$this->uniquePerforceClientName = $this->generateUniquePerforceClientName();
if (!$repoConfig) {
return;
}
if (isset($repoConfig['unique_perforce_client_name'])) {
$this->uniquePerforceClientName = $repoConfig['unique_perforce_client_name'];
}
if (isset($repoConfig['depot'])) {
$this->p4Depot = $repoConfig['depot'];
}
if (isset($repoConfig['branch'])) {
$this->p4Branch = $repoConfig['branch'];
}
if (isset($repoConfig['p4user'])) {
$this->p4User = $repoConfig['p4user'];
}
else {
$this->p4User = $this->getP4variable('P4USER');
}
if (isset($repoConfig['p4password'])) {
$this->p4Password = $repoConfig['p4password'];
}
}
public function initializeDepotAndBranch(?string $depot, ?string $branch) : void {
if (isset($depot)) {
$this->p4Depot = $depot;
}
if (isset($branch)) {
$this->p4Branch = $branch;
}
}
/**
* @return non-empty-string
*/
public function generateUniquePerforceClientName() : string {
return gethostname() . "_" . time();
}
public function cleanupClientSpec() : void {
$client = $this->getClient();
$task = 'client -d ' . ProcessExecutor::escape($client);
$useP4Client = false;
$command = $this->generateP4Command($task, $useP4Client);
$this->executeCommand($command);
$clientSpec = $this->getP4ClientSpec();
$fileSystem = $this->getFilesystem();
$fileSystem->remove($clientSpec);
}
/**
* @param non-empty-string $command
*/
protected function executeCommand($command) : int {
$this->commandResult = '';
return $this->process
->execute($command, $this->commandResult);
}
public function getClient() : string {
if (!isset($this->p4Client)) {
$cleanStreamName = str_replace([
'//',
'/',
'@',
], [
'',
'_',
'',
], $this->getStream());
$this->p4Client = 'composer_perforce_' . $this->uniquePerforceClientName . '_' . $cleanStreamName;
}
return $this->p4Client;
}
protected function getPath() : string {
return $this->path;
}
public function initializePath(string $path) : void {
$this->path = $path;
$fs = $this->getFilesystem();
$fs->ensureDirectoryExists($path);
}
protected function getPort() : string {
return $this->p4Port;
}
public function setStream(string $stream) : void {
$this->p4Stream = $stream;
$index = strrpos($stream, '/');
//Stream format is //depot/stream, while non-streaming depot is //depot
if ($index > 2) {
$this->p4DepotType = 'stream';
}
}
public function isStream() : bool {
return is_string($this->p4DepotType) && strcmp($this->p4DepotType, 'stream') === 0;
}
public function getStream() : string {
if (!isset($this->p4Stream)) {
if ($this->isStream()) {
$this->p4Stream = '//' . $this->p4Depot . '/' . $this->p4Branch;
}
else {
$this->p4Stream = '//' . $this->p4Depot;
}
}
return $this->p4Stream;
}
public function getStreamWithoutLabel(string $stream) : string {
$index = strpos($stream, '@');
if ($index === false) {
return $stream;
}
return substr($stream, 0, $index);
}
/**
* @return non-empty-string
*/
public function getP4ClientSpec() : string {
return $this->path . '/' . $this->getClient() . '.p4.spec';
}
public function getUser() : ?string {
return $this->p4User;
}
public function setUser(?string $user) : void {
$this->p4User = $user;
}
public function queryP4User() : void {
$this->getUser();
if (strlen((string) $this->p4User) > 0) {
return;
}
$this->p4User = $this->getP4variable('P4USER');
if (strlen((string) $this->p4User) > 0) {
return;
}
$this->p4User = $this->io
->ask('Enter P4 User:');
if ($this->windowsFlag) {
$command = $this->getP4Executable() . ' set P4USER=' . $this->p4User;
}
else {
$command = 'export P4USER=' . $this->p4User;
}
$this->executeCommand($command);
}
/**
* @return ?string
*/
protected function getP4variable(string $name) : ?string {
if ($this->windowsFlag) {
$command = $this->getP4Executable() . ' set';
$this->executeCommand($command);
$result = trim($this->commandResult);
$resArray = explode(PHP_EOL, $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;
}
}
return null;
}
$command = 'echo $' . $name;
$this->executeCommand($command);
$result = trim($this->commandResult);
return $result;
}
public function queryP4Password() : ?string {
if (isset($this->p4Password)) {
return $this->p4Password;
}
$password = $this->getP4variable('P4PASSWD');
if (strlen((string) $password) <= 0) {
$password = $this->io
->askAndHideAnswer('Enter password for Perforce user ' . $this->getUser() . ': ');
}
$this->p4Password = $password;
return $password;
}
/**
* @return non-empty-string
*/
public function generateP4Command(string $command, bool $useClient = true) : string {
$p4Command = $this->getP4Executable() . ' ';
$p4Command .= '-u ' . $this->getUser() . ' ';
if ($useClient) {
$p4Command .= '-c ' . $this->getClient() . ' ';
}
$p4Command .= '-p ' . $this->getPort() . ' ' . $command;
return $p4Command;
}
public function isLoggedIn() : bool {
$command = $this->generateP4Command('login -s', false);
$exitCode = $this->executeCommand($command);
if ($exitCode) {
$errorOutput = $this->process
->getErrorOutput();
$index = strpos($errorOutput, $this->getUser());
if ($index === false) {
$index = strpos($errorOutput, 'p4');
if ($index === false) {
return false;
}
throw new \Exception('p4 command not found in path: ' . $errorOutput);
}
throw new \Exception('Invalid user name: ' . $this->getUser());
}
return true;
}
public function connectClient() : void {
$p4CreateClientCommand = $this->generateP4Command('client -i < ' . ProcessExecutor::escape($this->getP4ClientSpec()));
$this->executeCommand($p4CreateClientCommand);
}
public function syncCodeBase(?string $sourceReference) : void {
$prevDir = Platform::getCwd();
chdir($this->path);
$p4SyncCommand = $this->generateP4Command('sync -f ');
if (null !== $sourceReference) {
$p4SyncCommand .= '@' . $sourceReference;
}
$this->executeCommand($p4SyncCommand);
chdir($prevDir);
}
/**
* @param resource|false $spec
*/
public function writeClientSpecToFile($spec) : void {
fwrite($spec, 'Client: ' . $this->getClient() . PHP_EOL . PHP_EOL);
fwrite($spec, 'Update: ' . date('Y/m/d H:i:s') . PHP_EOL . PHP_EOL);
fwrite($spec, 'Access: ' . date('Y/m/d H:i:s') . PHP_EOL);
fwrite($spec, 'Owner: ' . $this->getUser() . PHP_EOL . PHP_EOL);
fwrite($spec, 'Description:' . PHP_EOL);
fwrite($spec, ' Created by ' . $this->getUser() . ' from composer.' . PHP_EOL . PHP_EOL);
fwrite($spec, 'Root: ' . $this->getPath() . PHP_EOL . PHP_EOL);
fwrite($spec, 'Options: noallwrite noclobber nocompress unlocked modtime rmdir' . PHP_EOL . PHP_EOL);
fwrite($spec, 'SubmitOptions: revertunchanged' . PHP_EOL . PHP_EOL);
fwrite($spec, 'LineEnd: local' . PHP_EOL . PHP_EOL);
if ($this->isStream()) {
fwrite($spec, 'Stream:' . PHP_EOL);
fwrite($spec, ' ' . $this->getStreamWithoutLabel($this->p4Stream) . PHP_EOL);
}
else {
fwrite($spec, 'View: ' . $this->getStream() . '/... //' . $this->getClient() . '/... ' . PHP_EOL);
}
}
public function writeP4ClientSpec() : void {
$clientSpec = $this->getP4ClientSpec();
$spec = fopen($clientSpec, 'w');
try {
$this->writeClientSpecToFile($spec);
} catch (\Exception $e) {
fclose($spec);
throw $e;
}
fclose($spec);
}
/**
* @param resource $pipe
* @param mixed $name
*/
protected function read($pipe, $name) : void {
if (feof($pipe)) {
return;
}
$line = fgets($pipe);
while ($line !== false) {
$line = fgets($pipe);
}
}
public function windowsLogin(?string $password) : int {
$command = $this->generateP4Command(' login -a');
$process = Process::fromShellCommandline($command, null, null, $password);
return $process->run();
}
public function p4Login() : void {
$this->queryP4User();
if (!$this->isLoggedIn()) {
$password = $this->queryP4Password();
if ($this->windowsFlag) {
$this->windowsLogin($password);
}
else {
$command = 'echo ' . ProcessExecutor::escape($password) . ' | ' . $this->generateP4Command(' login -a', false);
$exitCode = $this->executeCommand($command);
if ($exitCode) {
throw new \Exception("Error logging in:" . $this->process
->getErrorOutput());
}
}
}
}
/**
* @return mixed[]|null
*/
public function getComposerInformation(string $identifier) : ?array {
$composerFileContent = $this->getFileContent('composer.json', $identifier);
if (!$composerFileContent) {
return null;
}
return json_decode($composerFileContent, true);
}
public function getFileContent(string $file, string $identifier) : ?string {
$path = $this->getFilePath($file, $identifier);
$command = $this->generateP4Command(' print ' . ProcessExecutor::escape($path));
$this->executeCommand($command);
$result = $this->commandResult;
if (!trim($result)) {
return null;
}
return $result;
}
public function getFilePath(string $file, string $identifier) : ?string {
$index = strpos($identifier, '@');
if ($index === false) {
return $identifier . '/' . $file;
}
$path = substr($identifier, 0, $index) . '/' . $file . substr($identifier, $index);
$command = $this->generateP4Command(' files ' . ProcessExecutor::escape($path), false);
$this->executeCommand($command);
$result = $this->commandResult;
$index2 = strpos($result, 'no such file(s).');
if ($index2 === false) {
$index3 = strpos($result, 'change');
if ($index3 !== false) {
$phrase = trim(substr($result, $index3));
$fields = explode(' ', $phrase);
return substr($identifier, 0, $index) . '/' . $file . '@' . $fields[1];
}
}
return null;
}
/**
* @return array{master: string}
*/
public function getBranches() : array {
$possibleBranches = [];
if (!$this->isStream()) {
$possibleBranches[$this->p4Branch] = $this->getStream();
}
else {
$command = $this->generateP4Command('streams ' . ProcessExecutor::escape('//' . $this->p4Depot . '/...'));
$this->executeCommand($command);
$result = $this->commandResult;
$resArray = explode(PHP_EOL, $result);
foreach ($resArray as $line) {
$resBits = explode(' ', $line);
if (count($resBits) > 4) {
$branch = Preg::replace('/[^A-Za-z0-9 ]/', '', $resBits[4]);
$possibleBranches[$branch] = $resBits[1];
}
}
}
$command = $this->generateP4Command('changes ' . ProcessExecutor::escape($this->getStream() . '/...'), false);
$this->executeCommand($command);
$result = $this->commandResult;
$resArray = explode(PHP_EOL, $result);
$lastCommit = $resArray[0];
$lastCommitArr = explode(' ', $lastCommit);
$lastCommitNum = $lastCommitArr[1];
return [
'master' => $possibleBranches[$this->p4Branch] . '@' . $lastCommitNum,
];
}
/**
* @return array<string, string>
*/
public function getTags() : array {
$command = $this->generateP4Command('labels');
$this->executeCommand($command);
$result = $this->commandResult;
$resArray = explode(PHP_EOL, $result);
$tags = [];
foreach ($resArray as $line) {
if (strpos($line, 'Label') !== false) {
$fields = explode(' ', $line);
$tags[$fields[1]] = $this->getStream() . '@' . $fields[1];
}
}
return $tags;
}
public function checkStream() : bool {
$command = $this->generateP4Command('depots', false);
$this->executeCommand($command);
$result = $this->commandResult;
$resArray = explode(PHP_EOL, $result);
foreach ($resArray as $line) {
if (strpos($line, 'Depot') !== false) {
$fields = explode(' ', $line);
if (strcmp($this->p4Depot, $fields[1]) === 0) {
$this->p4DepotType = $fields[3];
return $this->isStream();
}
}
}
return false;
}
/**
* @return mixed|null
*/
protected function getChangeList(string $reference) : mixed {
$index = strpos($reference, '@');
if ($index === false) {
return null;
}
$label = substr($reference, $index);
$command = $this->generateP4Command(' changes -m1 ' . ProcessExecutor::escape($label));
$this->executeCommand($command);
$changes = $this->commandResult;
if (strpos($changes, 'Change') !== 0) {
return null;
}
$fields = explode(' ', $changes);
return $fields[1];
}
/**
* @return mixed|null
*/
public function getCommitLogs(string $fromReference, string $toReference) : mixed {
$fromChangeList = $this->getChangeList($fromReference);
if ($fromChangeList === null) {
return null;
}
$toChangeList = $this->getChangeList($toReference);
if ($toChangeList === null) {
return null;
}
$index = strpos($fromReference, '@');
$main = substr($fromReference, 0, $index) . '/...';
$command = $this->generateP4Command('filelog ' . ProcessExecutor::escape($main . '@' . $fromChangeList . ',' . $toChangeList));
$this->executeCommand($command);
return $this->commandResult;
}
public function getFilesystem() : Filesystem {
if (null === $this->filesystem) {
$this->filesystem = new Filesystem($this->process);
}
return $this->filesystem;
}
public function setFilesystem(Filesystem $fs) : void {
$this->filesystem = $fs;
}
private function getP4Executable() : string {
static $p4Executable;
if ($p4Executable) {
return $p4Executable;
}
$finder = new ExecutableFinder();
return $p4Executable = $finder->find('p4') ?? 'p4';
}
}
Members
Title Sort descending | Modifiers | Object type | Summary |
---|---|---|---|
Perforce::$commandResult | protected | property | @var string |
Perforce::$filesystem | protected | property | @var ?Filesystem |
Perforce::$io | protected | property | @var IOInterface |
Perforce::$p4Branch | protected | property | @var ?string |
Perforce::$p4Client | protected | property | @var ?string |
Perforce::$p4ClientSpec | protected | property | @var string |
Perforce::$p4Depot | protected | property | @var ?string |
Perforce::$p4DepotType | protected | property | @var ?string |
Perforce::$p4Password | protected | property | @var ?string |
Perforce::$p4Port | protected | property | @var string |
Perforce::$p4Stream | protected | property | @var ?string |
Perforce::$p4User | protected | property | @var ?string |
Perforce::$path | protected | property | @var string |
Perforce::$process | protected | property | @var ProcessExecutor |
Perforce::$uniquePerforceClientName | protected | property | @var string |
Perforce::$windowsFlag | protected | property | @var bool |
Perforce::checkServerExists | public static | function | |
Perforce::checkStream | public | function | |
Perforce::cleanupClientSpec | public | function | |
Perforce::connectClient | public | function | |
Perforce::create | public static | function | @phpstan-param RepoConfig $repoConfig |
Perforce::executeCommand | protected | function | |
Perforce::generateP4Command | public | function | |
Perforce::generateUniquePerforceClientName | public | function | |
Perforce::getBranches | public | function | |
Perforce::getChangeList | protected | function | |
Perforce::getClient | public | function | |
Perforce::getCommitLogs | public | function | |
Perforce::getComposerInformation | public | function | |
Perforce::getFileContent | public | function | |
Perforce::getFilePath | public | function | |
Perforce::getFilesystem | public | function | |
Perforce::getP4ClientSpec | public | function | |
Perforce::getP4Executable | private | function | |
Perforce::getP4variable | protected | function | |
Perforce::getPath | protected | function | |
Perforce::getPort | protected | function | |
Perforce::getStream | public | function | |
Perforce::getStreamWithoutLabel | public | function | |
Perforce::getTags | public | function | |
Perforce::getUser | public | function | |
Perforce::initialize | public | function | @phpstan-param RepoConfig $repoConfig |
Perforce::initializeDepotAndBranch | public | function | |
Perforce::initializePath | public | function | |
Perforce::isLoggedIn | public | function | |
Perforce::isStream | public | function | |
Perforce::p4Login | public | function | |
Perforce::queryP4Password | public | function | |
Perforce::queryP4User | public | function | |
Perforce::read | protected | function | |
Perforce::setFilesystem | public | function | |
Perforce::setStream | public | function | |
Perforce::setUser | public | function | |
Perforce::syncCodeBase | public | function | |
Perforce::windowsLogin | public | function | |
Perforce::writeClientSpecToFile | public | function | |
Perforce::writeP4ClientSpec | public | function | |
Perforce::__construct | public | function | @phpstan-param RepoConfig $repoConfig |