Skip to content

Commit 5b8552d

Browse files
committed
Add support for getting type and version from lock file for local package queries
1 parent bb58e20 commit 5b8552d

10 files changed

+270
-54
lines changed

src/Commands/Show.php

+78-15
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,17 @@ public function execute()
6565
foreach ($results as $result) {
6666
[$namespace, $name] = $this->nameSplit($result['name']);
6767

68+
if ($this->mode->isLocal() && $this->composer->getLockFile()->exists()) {
69+
$result['version'] = $this->composer->getLockFile()->getVersion($namespace, $name);
70+
$result['type'] = $this->composer->getLockFile()->getType($namespace, $name);
71+
}
72+
6873
if (isset($result['version'])) {
6974
$packages[] = Composer::newVersionedPackage(
7075
$namespace,
7176
$name,
7277
$result['description'] ?? '',
78+
$result['type'] ?? '',
7379
$result['version'],
7480
$result['latest'] ?? '',
7581
VersionStatus::tryFrom($result['latest-status'] ?? '') ?? VersionStatus::UP_TO_DATE
@@ -79,6 +85,7 @@ public function execute()
7985
$namespace,
8086
$name,
8187
$result['description'] ?? '',
88+
$result['type'] ?? '',
8289
);
8390
}
8491
}
@@ -113,21 +120,77 @@ public function execute()
113120
$result = $results;
114121
[$namespace, $name] = $this->nameSplit($result['name']);
115122

116-
return Composer::newDetailedPackage(
117-
$namespace,
118-
$name,
119-
$result['description'] ?? '',
120-
$result['type'] ?? 'library',
121-
$result['keywords'] ?? [],
122-
$result['homepage'] ?? '',
123-
$result['authors'] ?? [],
124-
$result['licenses'] ?? [],
125-
$result['support'] ?? [],
126-
$result['funding'] ?? [],
127-
$result['requires'] ?? [],
128-
$result['devRequires'] ?? [],
129-
$result['extras'] ?? [],
130-
);
123+
if ($this->mode->isLocal() && $this->composer->getLockFile()->exists()) {
124+
$result['version'] = $this->composer->getLockFile()->getVersion($namespace, $name);
125+
$result['type'] = $this->composer->getLockFile()->getType($namespace, $name);
126+
}
127+
128+
if (
129+
isset($result['licenses'])
130+
|| isset($result['authors'])
131+
|| isset($result['requires'])
132+
|| isset($result['require-dev'])
133+
) {
134+
if (isset($result['version'])) {
135+
return Composer::newDetailedVersionedPackage(
136+
$namespace,
137+
$name,
138+
description: $result['description'] ?? '',
139+
keywords: $result['keywords'] ?? [],
140+
type: $result['type'] ?? 'library',
141+
homepage: $result['homepage'] ?? '',
142+
authors: $result['authors'] ?? [],
143+
licenses: $result['licenses'] ?? [],
144+
support: $result['support'] ?? [],
145+
funding: $result['funding'] ?? [],
146+
requires: $result['require'] ?? [],
147+
devRequires: $result['require-dev'] ?? [],
148+
extras: $result['extra'] ?? [],
149+
conflicts: $result['conflict'] ?? [],
150+
replaces: $result['replace'] ?? [],
151+
readme: $result['readme'] ?? '',
152+
version: $result['version'],
153+
);
154+
} else {
155+
return Composer::newDetailedPackage(
156+
$namespace,
157+
$name,
158+
description: $result['description'] ?? '',
159+
keywords: $result['keywords'] ?? [],
160+
type: $result['type'] ?? 'library',
161+
homepage: $result['homepage'] ?? '',
162+
authors: $result['authors'] ?? [],
163+
licenses: $result['licenses'] ?? [],
164+
support: $result['support'] ?? [],
165+
funding: $result['funding'] ?? [],
166+
requires: $result['require'] ?? [],
167+
devRequires: $result['require-dev'] ?? [],
168+
extras: $result['extra'] ?? [],
169+
conflicts: $result['conflict'] ?? [],
170+
replaces: $result['replace'] ?? [],
171+
readme: $result['readme'] ?? '',
172+
);
173+
}
174+
} else {
175+
if (isset($result['version'])) {
176+
return Composer::newVersionedPackage(
177+
$namespace,
178+
$name,
179+
$result['description'] ?? '',
180+
$result['type'] ?? '',
181+
$result['version'],
182+
$result['latest'] ?? '',
183+
VersionStatus::tryFrom($result['latest-status'] ?? '') ?? VersionStatus::UP_TO_DATE
184+
);
185+
} else {
186+
return Composer::newPackage(
187+
$namespace,
188+
$name,
189+
$result['description'] ?? '',
190+
$result['type'] ?? '',
191+
);
192+
}
193+
}
131194
}
132195

133196
return null;

src/Composer.php

+59-15
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Winter\Packager\Package\Constraint;
99
use Winter\Packager\Package\DetailedPackage;
1010
use Winter\Packager\Package\DetailedVersionedPackage;
11+
use Winter\Packager\Package\LockFile;
1112
use Winter\Packager\Package\Package;
1213
use Winter\Packager\Package\Packagist;
1314
use Winter\Packager\Package\VersionedPackage;
@@ -30,34 +31,44 @@
3031
class Composer
3132
{
3233
/**
33-
* @var string The path to the Composer home directory (where settings and cached dependencies are kept).
34+
* The path to the Composer home directory (where settings and cached dependencies are kept).
3435
*/
35-
protected $homeDir;
36+
protected string $homeDir;
3637

3738
/**
38-
* @var string The path to working directory where the project will be built.
39+
* The path to working directory where the project will be built.
3940
*/
40-
protected $workDir;
41+
protected string $workDir;
4142

4243
/**
43-
* @var string The name of the JSON configuration file.
44+
* The name of the JSON configuration file.
4445
*/
45-
protected $configFile = 'composer.json';
46+
protected string $configFile = 'composer.json';
4647

4748
/**
48-
* @var string The name of the dependency directory.
49+
* The name of the Composer lock file.
4950
*/
50-
protected $vendorDir = 'vendor';
51+
protected string $lockFile = 'composer.lock';
5152

5253
/**
53-
* @var int The process timeout, in seconds.
54+
* An instance of the lock file class.
5455
*/
55-
protected $timeout = 300;
56+
protected ?LockFile $lockFileInstance = null;
5657

5758
/**
58-
* @var int The memory limit, in MBytes.
59+
* The name of the dependency directory.
5960
*/
60-
protected $memoryLimit = 1536;
61+
protected string $vendorDir = 'vendor';
62+
63+
/**
64+
* The process timeout, in seconds.
65+
*/
66+
protected int $timeout = 300;
67+
68+
/**
69+
* The memory limit, in MBytes.
70+
*/
71+
protected int $memoryLimit = 1536;
6172

6273
/**
6374
* The current behaviour for handling abandoned packages.
@@ -67,7 +78,7 @@ class Composer
6778
/**
6879
* @var array<string, string|Command> A list of supported commands
6980
*/
70-
protected $commands = [
81+
protected array $commands = [
7182
'i' => \Winter\Packager\Commands\Install::class,
7283
'install' => \Winter\Packager\Commands\Install::class,
7384
'search' => \Winter\Packager\Commands\Search::class,
@@ -80,7 +91,7 @@ class Composer
8091
* @var array<string, string> Map of classes to use for packages, constraints and collections. This allows for
8192
* custom classes to be used for these objects, should a developer wish to extend the functionality.
8293
*/
83-
protected static $packageClasses = [
94+
protected static array $packageClasses = [
8495
'package' => \Winter\Packager\Package\Package::class,
8596
'versionedPackage' => \Winter\Packager\Package\VersionedPackage::class,
8697
'detailedPackage' => \Winter\Packager\Package\DetailedPackage::class,
@@ -95,7 +106,7 @@ class Composer
95106
* @param string $workingDir The working directory where the "composer.json" file is located.
96107
* @param string $homeDir The Composer home directory.
97108
*/
98-
public function __construct(string $workingDir = null, string $homeDir = null)
109+
public function __construct(string $workingDir = '', string $homeDir = '')
99110
{
100111
$this->workDir = $workingDir;
101112
$this->homeDir = $homeDir;
@@ -251,6 +262,39 @@ public function setConfigFile(string $configFile): static
251262
return $this;
252263
}
253264

265+
/**
266+
* Gets the name for the lock file, where the Composer package dependencies are stored.
267+
*
268+
* By default, this is "composer.lock".
269+
*/
270+
public function getLockFilename(): string
271+
{
272+
return $this->lockFile;
273+
}
274+
275+
/**
276+
* Sets the name for the lock file, where the Composer package dependencies are stored.
277+
*
278+
* @param string $lockFile Lock file name.
279+
*/
280+
public function setLockFile(string $lockFile): static
281+
{
282+
$this->lockFile = $lockFile;
283+
return $this;
284+
}
285+
286+
/**
287+
* Gets an instance of the LockFile class to read the Composer lock file.
288+
*/
289+
public function getLockFile(): LockFile
290+
{
291+
if (!isset($this->lockFileInstance)) {
292+
$this->lockFileInstance = new LockFile($this);
293+
}
294+
295+
return $this->lockFileInstance;
296+
}
297+
254298
/**
255299
* Gets the name for the vendor package directory.
256300
*

src/Enums/ShowMode.php

+13
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,19 @@ public function isCollectible(): bool
6464
]);
6565
}
6666

67+
/**
68+
* Determines if this mode queries only local packages, and can support querying of the lock file.
69+
*/
70+
public function isLocal(): bool
71+
{
72+
return in_array($this, [
73+
static::INSTALLED,
74+
static::LOCKED,
75+
static::OUTDATED,
76+
static::DIRECT,
77+
]);
78+
}
79+
6780
/**
6881
* Gets the array key we expect from the JSON output from the Composer `show` command.
6982
*/

src/Package/Collection.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ public function toArray()
190190
/**
191191
* Converts all packages within the collection to detailed packages.
192192
*/
193-
public function toDetailed()
193+
public function toDetailed(): static
194194
{
195195
foreach ($this->items as &$package) {
196196
if ($package instanceof DetailedPackage) {

src/Package/DetailedPackage.php

+1-11
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,7 @@ public function __construct(
4242
protected array $replaces = [],
4343
protected string $readme = '',
4444
) {
45-
parent::__construct($namespace, $name, $description);
46-
}
47-
48-
public function getType(): string
49-
{
50-
return $this->type;
51-
}
52-
53-
public function setType(string $type): void
54-
{
55-
$this->type = $type;
45+
parent::__construct($namespace, $name, $description, $type);
5646
}
5747

5848
/**

src/Package/LockFile.php

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
namespace Winter\Packager\Package;
4+
5+
use Winter\Packager\Composer;
6+
7+
/**
8+
* Lock file class.
9+
*
10+
* This class provides functionality for reading the Composer lock file. This is used to determine (some) details about
11+
* the packages that are installed in the current project - mainly limited to versioning and type information.
12+
*
13+
* We prefer to rely on the Packagist API for most information, as those details are specifically provided by the
14+
* authors of the library, whereas the lock file is local and easily manipulated.
15+
*
16+
* @author Ben Thomson <[email protected]>
17+
* @since 0.3.0
18+
*/
19+
class LockFile
20+
{
21+
protected bool $exists = false;
22+
23+
/**
24+
* @var array<string, array<string, string>> Collated package information.
25+
*/
26+
protected array $packages = [];
27+
28+
public function __construct(
29+
protected Composer $composer,
30+
) {
31+
if (file_exists(
32+
rtrim($this->composer->getworkDir(), DIRECTORY_SEPARATOR)
33+
. DIRECTORY_SEPARATOR
34+
. $this->composer->getLockFilename()
35+
)) {
36+
$this->exists = true;
37+
$this->collatePackageInfo();
38+
}
39+
}
40+
41+
public function exists(): bool
42+
{
43+
return $this->exists;
44+
}
45+
46+
public function getVersion(string $namespace, string $name): ?string
47+
{
48+
if (!array_key_exists($namespace . '/' . $name, $this->packages)) {
49+
return null;
50+
}
51+
52+
return $this->packages[$namespace . '/' . $name]['version'];
53+
}
54+
55+
public function getType(string $namespace, string $name): ?string
56+
{
57+
if (!array_key_exists($namespace . '/' . $name, $this->packages)) {
58+
return null;
59+
}
60+
61+
return $this->packages[$namespace . '/' . $name]['type'];
62+
}
63+
64+
protected function collatePackageInfo(): void
65+
{
66+
$lockFile = json_decode(
67+
file_get_contents(
68+
rtrim($this->composer->getworkDir(), DIRECTORY_SEPARATOR)
69+
. DIRECTORY_SEPARATOR
70+
. $this->composer->getLockFilename()
71+
),
72+
true
73+
);
74+
75+
foreach ($lockFile['packages'] as $package) {
76+
$this->packages[$package['name']] = [
77+
'version' => $package['version'],
78+
'type' => $package['type'],
79+
];
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)