Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x
  2. Perforce.php

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\Util
View 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

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal