Skip to content

Commit 803a9ef

Browse files
committed
Refactor packages.
Packages are now treated as individual instances of a Package class, similar to how Composer does it internally, which will help us to extend and format any output from Composer itself.
1 parent 4e46213 commit 803a9ef

File tree

10 files changed

+661
-70
lines changed

10 files changed

+661
-70
lines changed

src/Commands/Search.php

+15-25
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace Winter\Packager\Commands;
44

55
use Winter\Packager\Exceptions\CommandException;
6+
use Winter\Packager\Package\Package;
7+
use Winter\Packager\Package\Collection;
68

79
/**
810
* Search command.
@@ -34,11 +36,6 @@ class Search extends BaseCommand
3436
*/
3537
public ?string $limitTo = null;
3638

37-
/**
38-
* @var array<int, mixed> The results returned from the query.
39-
*/
40-
public array $results = [];
41-
4239
/**
4340
* Command handler.
4441
*/
@@ -69,9 +66,20 @@ public function execute()
6966
throw new CommandException(implode(PHP_EOL, $output['output']));
7067
}
7168

72-
$this->results = json_decode(implode(PHP_EOL, $output['output']), true);
69+
$results = json_decode(implode(PHP_EOL, $output['output']), true);
70+
$packages = [];
7371

74-
return $this;
72+
foreach ($results as $result) {
73+
[$namespace, $name] = preg_split('/\//', $result['name'], 2);
74+
75+
$packages[] = new Package(
76+
$namespace,
77+
$name,
78+
$result['description'] ?? ''
79+
);
80+
}
81+
82+
return new Collection($packages);
7583
}
7684

7785
/**
@@ -90,24 +98,6 @@ public function requiresWorkDir(): bool
9098
return false;
9199
}
92100

93-
/**
94-
* Returns the list of results found.
95-
*
96-
* @return array<int, mixed>
97-
*/
98-
public function getResults(): array
99-
{
100-
return $this->results;
101-
}
102-
103-
/**
104-
* Returns the number of results found.
105-
*/
106-
public function count(): int
107-
{
108-
return count($this->results);
109-
}
110-
111101
/**
112102
* @inheritDoc
113103
*/

src/Commands/Show.php

+100-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22

33
namespace Winter\Packager\Commands;
44

5+
use Winter\Packager\Enums\VersionStatus;
56
use Winter\Packager\Exceptions\CommandException;
7+
use Winter\Packager\Package\Collection;
8+
use Winter\Packager\Package\DetailedPackage;
9+
use Winter\Packager\Package\Package;
10+
use Winter\Packager\Package\VersionedPackage;
611

712
/**
813
* Show command.
@@ -98,7 +103,91 @@ public function execute()
98103
}
99104
}
100105

101-
return json_decode(implode(PHP_EOL, $output['output']), true);
106+
$results = json_decode(implode(PHP_EOL, $output['output']), true);
107+
$packages = [];
108+
109+
if (is_null($this->package) && in_array($this->mode, ['installed', 'locked', 'platform', 'path', 'outdated', 'direct'])) {
110+
// Convert packages in simple lists to a package collection
111+
$key = (!in_array($this->mode, ['locked', 'platform'])) ? 'installed' : $this->mode;
112+
$results = $results[$key];
113+
114+
foreach ($results as $result) {
115+
[$namespace, $name] = $this->nameSplit($result['name']);
116+
117+
if (isset($result['version'])) {
118+
$packages[] = new VersionedPackage(
119+
$namespace,
120+
$name,
121+
$result['description'] ?? '',
122+
$result['version'],
123+
$result['latest'] ?? '',
124+
VersionStatus::tryFrom($result['latest-status'] ?? '') ?? VersionStatus::UP_TO_DATE
125+
);
126+
} else {
127+
$packages[] = new Package(
128+
$namespace,
129+
$name,
130+
$result['description'] ?? '',
131+
);
132+
}
133+
}
134+
135+
return new Collection($packages);
136+
} elseif (is_null($this->package) && $this->mode === 'available') {
137+
// Convert entire available package list into a package collection
138+
foreach ($results['available'] as $result) {
139+
[$namespace, $name] = $this->nameSplit($result['name']);
140+
141+
$packages[] = new Package(
142+
$namespace,
143+
$name,
144+
$result['description'] ?? '',
145+
);
146+
}
147+
148+
return new Collection($packages);
149+
} elseif ($this->mode === 'self') {
150+
$result = $results;
151+
[$namespace, $name] = $this->nameSplit($result['name']);
152+
153+
// Return the current package
154+
return new DetailedPackage(
155+
$namespace,
156+
$name,
157+
$result['description'] ?? '',
158+
$result['type'] ?? 'library',
159+
$result['keywords'] ?? [],
160+
$result['homepage'] ?? '',
161+
$result['authors'] ?? [],
162+
$result['licenses'] ?? [],
163+
$result['support'] ?? [],
164+
$result['funding'] ?? [],
165+
$result['requires'] ?? [],
166+
$result['devRequires'] ?? [],
167+
$result['extras'] ?? [],
168+
);
169+
} elseif (!is_null($this->package)) {
170+
$result = $results;
171+
[$namespace, $name] = $this->nameSplit($result['name']);
172+
173+
return new DetailedPackage(
174+
$namespace,
175+
$name,
176+
$result['description'] ?? '',
177+
$result['type'] ?? 'library',
178+
$result['keywords'] ?? [],
179+
$result['homepage'] ?? '',
180+
$result['authors'] ?? [],
181+
$result['licenses'] ?? [],
182+
$result['support'] ?? [],
183+
$result['funding'] ?? [],
184+
$result['requires'] ?? [],
185+
$result['devRequires'] ?? [],
186+
$result['extras'] ?? [],
187+
);
188+
}
189+
190+
return null;
102191
}
103192

104193
/**
@@ -140,4 +229,14 @@ public function arguments(): array
140229

141230
return $arguments;
142231
}
232+
233+
/**
234+
* Split package name from namespace.
235+
*
236+
* @return string[]
237+
*/
238+
protected function nameSplit(string $name): array
239+
{
240+
return preg_split('/\//', $name, 2);
241+
}
143242
}

src/Enums/VersionStatus.php

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Winter\Packager\Enums;
4+
5+
enum VersionStatus: string
6+
{
7+
case UP_TO_DATE = 'up-to-date';
8+
case SEMVER_UPDATE = 'semver-safe-update';
9+
case MAJOR_UPDATE = 'update-possible';
10+
}

src/Package/Collection.php

+189
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
<?php
2+
3+
namespace Winter\Packager\Package;
4+
5+
/**
6+
* Package collection.
7+
*
8+
* Collections contain one or more packages from a given result set, and can be used to filter and traverse the results.
9+
*
10+
* @author Ben Thomson <[email protected]>
11+
* @since 0.3.0
12+
* @implements \ArrayAccess<int|string, Package>
13+
* @implements \Iterator<int, Package>
14+
*/
15+
class Collection implements \ArrayAccess, \Iterator, \Countable
16+
{
17+
/**
18+
* @var array<int, Package> The packages contained in the collection.
19+
*/
20+
protected array $items = [];
21+
22+
/**
23+
* Present position in the collection.
24+
*/
25+
protected int $position = 0;
26+
27+
/**
28+
* Constructor.
29+
*
30+
* @param Package[]|Package $items
31+
*/
32+
public function __construct(...$items)
33+
{
34+
foreach ($items as $item) {
35+
if ($item instanceof Package) {
36+
$this->items[] = $item;
37+
} elseif (is_array($item)) {
38+
foreach ($item as $subItem) {
39+
if ($subItem instanceof Package) {
40+
$this->items[] = $subItem;
41+
}
42+
}
43+
}
44+
}
45+
}
46+
47+
/**
48+
* Adds a package to this collection.
49+
*/
50+
protected function add(Package $package): void
51+
{
52+
$this->items[] = $package;
53+
54+
uasort($this->items, function (Package $a, Package $b) {
55+
return $a->getPackageName() <=> $b->getPackageName();
56+
});
57+
}
58+
59+
/**
60+
* Gets the count of packages in this collection.
61+
*/
62+
public function count(): int
63+
{
64+
return count($this->items);
65+
}
66+
67+
public function rewind(): void
68+
{
69+
$this->position = 0;
70+
}
71+
72+
public function current(): mixed
73+
{
74+
return $this->items[$this->position];
75+
}
76+
77+
public function key(): int
78+
{
79+
return $this->position;
80+
}
81+
82+
public function next(): void
83+
{
84+
++$this->position;
85+
}
86+
87+
public function valid(): bool
88+
{
89+
return isset($this->items[$this->position]);
90+
}
91+
92+
/**
93+
* Gets a package at a given index.
94+
*
95+
* This does not reset the internal pointer of the collection.
96+
*
97+
* If no package is found at the given index, `null` is returned.
98+
*/
99+
public function get(int $index): ?Package
100+
{
101+
return $this->items[$index] ?? null;
102+
}
103+
104+
/**
105+
* Finds a given package in the collection.
106+
*/
107+
public function find(string $namespace, string $name = '', ?string $version = null): ?Package
108+
{
109+
if (empty($name) && strpos($namespace, '/') !== false) {
110+
[$namespace, $name] = explode('/', $namespace, 2);
111+
}
112+
113+
foreach ($this->items as $item) {
114+
if ($item->getNamespace() === $namespace && $item->getName() === $name) {
115+
if (is_null($version) || ($item instanceof VersionedPackage && $item->getVersion() === $version)) {
116+
return $item;
117+
}
118+
}
119+
}
120+
121+
return null;
122+
}
123+
124+
/**
125+
* Checks if a given offset exists.
126+
*
127+
* You may either provide an integer key to retrieve by index, or a string key in the format `namespace/name` to
128+
* find a particular package.
129+
*/
130+
public function offsetExists(mixed $offset): bool
131+
{
132+
if (is_int($offset)) {
133+
return isset($this->items[$offset]);
134+
}
135+
136+
if (is_string($offset)) {
137+
[$namespace, $name] = explode('/', $offset, 2);
138+
return !is_null($this->find($namespace, $name));
139+
}
140+
}
141+
142+
/**
143+
* Gets a package at a given offset.
144+
*
145+
* You may either provide an integer key to retrieve by index, or a string key in the format `namespace/name` to
146+
* find a particular package.
147+
*/
148+
public function offsetGet(mixed $offset): ?Package
149+
{
150+
if (is_int($offset)) {
151+
return $this->get($offset);
152+
}
153+
154+
if (is_string($offset)) {
155+
[$namespace, $name] = explode('/', $offset, 2);
156+
return $this->find($namespace, $name);
157+
}
158+
}
159+
160+
/**
161+
* Sets a package at a given offset.
162+
*
163+
* This method is not supported.
164+
*/
165+
public function offsetSet(mixed $offset, mixed $value): void
166+
{
167+
throw new \RuntimeException('You cannot set values in a package collection.');
168+
}
169+
170+
/**
171+
* Unsets a package at a given offset.
172+
*
173+
* This method is not supported.
174+
*/
175+
public function offsetUnset(mixed $offset): void
176+
{
177+
throw new \RuntimeException('You cannot unset values in a package collection.');
178+
}
179+
180+
/**
181+
* Retrieve the collection as an array.
182+
*
183+
* @return array<int, \Winter\Packager\Package\Package>
184+
*/
185+
public function toArray()
186+
{
187+
return $this->items;
188+
}
189+
}

0 commit comments

Comments
 (0)