class PathRepository
This repository allows installing local packages that are not necessarily under their own VCS.
The local packages will be symlinked when possible, else they will be copied.
"require": {
"<vendor>/<local-package>": "*"
},
"repositories": [
{
"type": "path",
"url": "../../relative/path/to/package/"
},
{
"type": "path",
"url": "/absolute/path/to/package/"
},
{
"type": "path",
"url": "/absolute/path/to/several/packages/*"
},
{
"type": "path",
"url": "../../relative/path/to/package/",
"options": {
"symlink": false
}
},
{
"type": "path",
"url": "../../relative/path/to/package/",
"options": {
"reference": "none"
}
},
]
@author Samuel Roze <samuel.roze@gmail.com> @author Johann Reinke <johann.reinke@gmail.com>
Hierarchy
- class \Composer\Repository\ArrayRepository implements \Composer\Repository\RepositoryInterface
- class \Composer\Repository\PathRepository extends \Composer\Repository\ArrayRepository implements \Composer\Repository\ConfigurableRepositoryInterface
Expanded class hierarchy of PathRepository
File
-
vendor/
composer/ composer/ src/ Composer/ Repository/ PathRepository.php, line 72
Namespace
Composer\RepositoryView source
class PathRepository extends ArrayRepository implements ConfigurableRepositoryInterface {
/**
* @var ArrayLoader
*/
private $loader;
/**
* @var VersionGuesser
*/
private $versionGuesser;
/**
* @var string
*/
private $url;
/**
* @var mixed[]
* @phpstan-var array{url: string, options?: array{symlink?: bool, reference?: string, relative?: bool, versions?: array<string, string>}}
*/
private $repoConfig;
/**
* @var ProcessExecutor
*/
private $process;
/**
* @var array{symlink?: bool, reference?: string, relative?: bool, versions?: array<string, string>}
*/
private $options;
/**
* Initializes path repository.
*
* @param array{url?: string, options?: array{symlink?: bool, reference?: string, relative?: bool, versions?: array<string, string>}} $repoConfig
*/
public function __construct(array $repoConfig, IOInterface $io, Config $config, ?HttpDownloader $httpDownloader = null, ?EventDispatcher $dispatcher = null, ?ProcessExecutor $process = null) {
if (!isset($repoConfig['url'])) {
throw new \RuntimeException('You must specify the `url` configuration for the path repository');
}
$this->loader = new ArrayLoader(null, true);
$this->url = Platform::expandPath($repoConfig['url']);
$this->process = $process ?? new ProcessExecutor($io);
$this->versionGuesser = new VersionGuesser($config, $this->process, new VersionParser(), $io);
$this->repoConfig = $repoConfig;
$this->options = $repoConfig['options'] ?? [];
if (!isset($this->options['relative'])) {
$filesystem = new Filesystem();
$this->options['relative'] = !$filesystem->isAbsolutePath($this->url);
}
parent::__construct();
}
public function getRepoName() : string {
return 'path repo (' . Url::sanitize($this->repoConfig['url']) . ')';
}
public function getRepoConfig() : array {
return $this->repoConfig;
}
/**
* Initializes path repository.
*
* This method will basically read the folder and add the found package.
*/
protected function initialize() : void {
parent::initialize();
$urlMatches = $this->getUrlMatches();
if (empty($urlMatches)) {
if (Preg::isMatch('{[*{}]}', $this->url)) {
$url = $this->url;
while (Preg::isMatch('{[*{}]}', $url)) {
$url = dirname($url);
}
// the parent directory before any wildcard exists, so we assume it is correctly configured but simply empty
if (is_dir($url)) {
return;
}
}
throw new \RuntimeException('The `url` supplied for the path (' . $this->url . ') repository does not exist');
}
foreach ($urlMatches as $url) {
$path = realpath($url) . DIRECTORY_SEPARATOR;
$composerFilePath = $path . 'composer.json';
if (!file_exists($composerFilePath)) {
continue;
}
$json = file_get_contents($composerFilePath);
$package = JsonFile::parseJson($json, $composerFilePath);
$package['dist'] = [
'type' => 'path',
'url' => $url,
];
$reference = $this->options['reference'] ?? 'auto';
if ('none' === $reference) {
$package['dist']['reference'] = null;
}
elseif ('config' === $reference || 'auto' === $reference) {
$package['dist']['reference'] = hash('sha1', $json . serialize($this->options));
}
// copy symlink/relative options to transport options
$package['transport-options'] = array_intersect_key($this->options, [
'symlink' => true,
'relative' => true,
]);
// use the version provided as option if available
if (isset($package['name'], $this->options['versions'][$package['name']])) {
$package['version'] = $this->options['versions'][$package['name']];
}
// carry over the root package version if this path repo is in the same git repository as root package
if (!isset($package['version']) && ($rootVersion = Platform::getEnv('COMPOSER_ROOT_VERSION'))) {
if (0 === $this->process
->execute([
'git',
'rev-parse',
'HEAD',
], $ref1, $path) && 0 === $this->process
->execute([
'git',
'rev-parse',
'HEAD',
], $ref2) && $ref1 === $ref2) {
$package['version'] = $this->versionGuesser
->getRootVersionFromEnv();
}
}
$output = '';
if ('auto' === $reference && is_dir($path . DIRECTORY_SEPARATOR . '.git') && 0 === $this->process
->execute(array_merge([
'git',
'log',
'-n1',
'--pretty=%H',
], GitUtil::getNoShowSignatureFlags($this->process)), $output, $path)) {
$package['dist']['reference'] = trim($output);
}
if (!isset($package['version'])) {
$versionData = $this->versionGuesser
->guessVersion($package, $path);
if (is_array($versionData) && $versionData['pretty_version']) {
// if there is a feature branch detected, we add a second packages with the feature branch version
if (!empty($versionData['feature_pretty_version'])) {
$package['version'] = $versionData['feature_pretty_version'];
$this->addPackage($this->loader
->load($package));
}
$package['version'] = $versionData['pretty_version'];
}
else {
$package['version'] = 'dev-main';
}
}
try {
$this->addPackage($this->loader
->load($package));
} catch (\Exception $e) {
throw new \RuntimeException('Failed loading the package in ' . $composerFilePath, 0, $e);
}
}
}
/**
* Get a list of all (possibly relative) path names matching given url (supports globbing).
*
* @return string[]
*/
private function getUrlMatches() : array {
$flags = GLOB_MARK | GLOB_ONLYDIR;
if (defined('GLOB_BRACE')) {
$flags |= GLOB_BRACE;
}
elseif (strpos($this->url, '{') !== false || strpos($this->url, '}') !== false) {
throw new \RuntimeException('The operating system does not support GLOB_BRACE which is required for the url ' . $this->url);
}
// Ensure environment-specific path separators are normalized to URL separators
return array_map(static function ($val) : string {
return rtrim(str_replace(DIRECTORY_SEPARATOR, '/', $val), '/');
}, glob($this->url, $flags));
}
}
Members
Title Sort descending | Modifiers | Object type | Summary | Overriden Title | Overrides |
---|---|---|---|---|---|
ArrayRepository::$packageMap | protected | property | |||
ArrayRepository::$packages | protected | property | @var ?array<BasePackage> | ||
ArrayRepository::addPackage | public | function | Adds a new package to the repository | 2 | |
ArrayRepository::count | public | function | Returns the number of packages in this repository | ||
ArrayRepository::createAliasPackage | protected | function | |||
ArrayRepository::findPackage | public | function | @inheritDoc | Overrides RepositoryInterface::findPackage | 1 |
ArrayRepository::findPackages | public | function | @inheritDoc | Overrides RepositoryInterface::findPackages | 1 |
ArrayRepository::getPackages | public | function | @inheritDoc | Overrides RepositoryInterface::getPackages | 1 |
ArrayRepository::getProviders | public | function | @inheritDoc | Overrides RepositoryInterface::getProviders | 1 |
ArrayRepository::hasPackage | public | function | @inheritDoc | Overrides RepositoryInterface::hasPackage | |
ArrayRepository::loadPackages | public | function | @inheritDoc | Overrides RepositoryInterface::loadPackages | 1 |
ArrayRepository::removePackage | public | function | Removes package from repository. | ||
ArrayRepository::search | public | function | @inheritDoc | Overrides RepositoryInterface::search | 2 |
PathRepository::$loader | private | property | |||
PathRepository::$options | private | property | |||
PathRepository::$process | private | property | |||
PathRepository::$repoConfig | private | property | @phpstan-var array{url: string, options?: array{symlink?: bool, reference?: string, relative?: bool, versions?: array<string, string>}} | ||
PathRepository::$url | private | property | |||
PathRepository::$versionGuesser | private | property | |||
PathRepository::getRepoConfig | public | function | Overrides ConfigurableRepositoryInterface::getRepoConfig | ||
PathRepository::getRepoName | public | function | Returns a name representing this repository to the user | Overrides ArrayRepository::getRepoName | |
PathRepository::getUrlMatches | private | function | Get a list of all (possibly relative) path names matching given url (supports globbing). | ||
PathRepository::initialize | protected | function | Initializes path repository. | Overrides ArrayRepository::initialize | |
PathRepository::__construct | public | function | Initializes path repository. | Overrides ArrayRepository::__construct | |
RepositoryInterface::SEARCH_FULLTEXT | public | constant | |||
RepositoryInterface::SEARCH_NAME | public | constant | |||
RepositoryInterface::SEARCH_VENDOR | public | constant |