Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions examples/getComposer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

require __DIR__.'/../vendor/autoload.php';

$client = new Packagist\Api\Client();
$package = $client->getComposer('sylius/sylius');

var_export($package);
8 changes: 8 additions & 0 deletions examples/getComposerLite.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

require __DIR__.'/../vendor/autoload.php';

$client = new Packagist\Api\Client();
$package = $client->getComposerLite('sylius/sylius');

var_export($package);
64 changes: 45 additions & 19 deletions spec/Packagist/Api/ClientSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use GuzzleHttp\Client as HttpClient;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\Stream;
use Packagist\Api\Client;
use Packagist\Api\Result\Factory;
use PhpSpec\ObjectBehavior;
Expand All @@ -17,15 +18,16 @@ public function let(HttpClient $client, Factory $factory)
$this->beConstructedWith($client, $factory);
}

public function it_is_initializable()
public function it_is_initializable(): void
{
$this->shouldHaveType(Client::class);
}

public function it_search_for_packages(HttpClient $client, Factory $factory, Response $response)
public function it_search_for_packages(HttpClient $client, Factory $factory, Response $response, Stream $body): void
{
$data = file_get_contents('spec/Packagist/Api/Fixture/search.json');
$response->getBody()->shouldBeCalled()->willReturn($data);
$response->getBody()->shouldBeCalled()->willReturn($body);
$body->getContents()->shouldBeCalled()->willReturn($data);

$client->request('GET', 'https://packagist.org/search.json?q=sylius')
->shouldBeCalled()
Expand All @@ -35,21 +37,23 @@ public function it_search_for_packages(HttpClient $client, Factory $factory, Res
$this->search('sylius');
}

public function it_search_for_packages_with_limit(HttpClient $client, Factory $factory, Response $response)
public function it_search_for_packages_with_limit(HttpClient $client, Factory $factory, Response $response, Stream $body): void
{
$data = file_get_contents('spec/Packagist/Api/Fixture/search.json');
$response->getBody()->shouldBeCalled()->willReturn($data);
$response->getBody()->shouldBeCalled()->willReturn($body);
$body->getContents()->shouldBeCalled()->willReturn($data);

$client->request('GET', 'https://packagist.org/search.json?q=sylius')->shouldBeCalled()->willReturn($response);
$factory->create(json_decode($data, true))->shouldBeCalled()->willReturn(array());

$this->search('sylius', [], 2);
}

public function it_searches_for_packages_with_filters(HttpClient $client, Factory $factory, Response $response)
public function it_searches_for_packages_with_filters(HttpClient $client, Factory $factory, Response $response, Stream $body): void
{
$data = file_get_contents('spec/Packagist/Api/Fixture/search.json');
$response->getBody()->shouldBeCalled()->willReturn($data);
$response->getBody()->shouldBeCalled()->willReturn($body);
$body->getContents()->shouldBeCalled()->willReturn($data);

$client->request('GET', 'https://packagist.org/search.json?tag=storage&q=sylius')
->shouldBeCalled()
Expand All @@ -60,10 +64,11 @@ public function it_searches_for_packages_with_filters(HttpClient $client, Factor
$this->search('sylius', ['tag' => 'storage']);
}

public function it_gets_popular_packages(HttpClient $client, Factory $factory, Response $response)
public function it_gets_popular_packages(HttpClient $client, Factory $factory, Response $response, Stream $body): void
{
$data = file_get_contents('spec/Packagist/Api/Fixture/popular.json');
$response->getBody()->shouldBeCalled()->willReturn($data);
$response->getBody()->shouldBeCalled()->willReturn($body);
$body->getContents()->shouldBeCalled()->willReturn($data);

$client->request('GET', 'https://packagist.org/explore/popular.json?page=1')
->shouldBeCalled()
Expand All @@ -76,10 +81,11 @@ public function it_gets_popular_packages(HttpClient $client, Factory $factory, R
$this->popular(2)->shouldHaveCount(2);
}

public function it_gets_package_details(HttpClient $client, Factory $factory, Response $response)
public function it_gets_package_details(HttpClient $client, Factory $factory, Response $response, Stream $body): void
{
$data = file_get_contents('spec/Packagist/Api/Fixture/get.json');
$response->getBody()->shouldBeCalled()->willReturn($data);
$response->getBody()->shouldBeCalled()->willReturn($body);
$body->getContents()->shouldBeCalled()->willReturn($data);

$client->request('GET', 'https://packagist.org/packages/sylius/sylius.json')
->shouldBeCalled()
Expand All @@ -90,10 +96,11 @@ public function it_gets_package_details(HttpClient $client, Factory $factory, Re
$this->get('sylius/sylius');
}

public function it_gets_composer_package_details(HttpClient $client, Factory $factory, Response $response)
public function it_gets_composer_package_details(HttpClient $client, Factory $factory, Response $response, Stream $body): void
{
$data = file_get_contents('spec/Packagist/Api/Fixture/get_composer.json');
$response->getBody()->shouldBeCalled()->willReturn($data);
$response->getBody()->shouldBeCalled()->willReturn($body);
$body->getContents()->shouldBeCalled()->willReturn($data);

$client->request('GET', 'https://packagist.org/p/sylius/sylius.json')
->shouldBeCalled()
Expand All @@ -104,10 +111,26 @@ public function it_gets_composer_package_details(HttpClient $client, Factory $fa
$this->getComposer('sylius/sylius');
}

public function it_lists_all_package_names(HttpClient $client, Factory $factory, Response $response)
public function it_gets_composer_lite_package_details(HttpClient $client, Factory $factory, Response $response, Stream $body): void
{
$data = file_get_contents('spec/Packagist/Api/Fixture/get_composer_lite.json');
$response->getBody()->shouldBeCalled()->willReturn($body);
$body->getContents()->shouldBeCalled()->willReturn($data);

$client->request('GET', 'https://packagist.org/p/sylius/sylius.json')
->shouldBeCalled()
->willReturn($response);

$factory->create(json_decode($data, true))->shouldBeCalled();

$this->getComposerLite('sylius/sylius');
}

public function it_lists_all_package_names(HttpClient $client, Factory $factory, Response $response, Stream $body): void
{
$data = file_get_contents('spec/Packagist/Api/Fixture/all.json');
$response->getBody()->shouldBeCalled()->willReturn($data);
$response->getBody()->shouldBeCalled()->willReturn($body);
$body->getContents()->shouldBeCalled()->willReturn($data);

$client->request('GET', 'https://packagist.org/packages/list.json')
->shouldBeCalled()
Expand All @@ -118,10 +141,11 @@ public function it_lists_all_package_names(HttpClient $client, Factory $factory,
$this->all();
}

public function it_filters_package_names_by_type(HttpClient $client, Factory $factory, Response $response)
public function it_filters_package_names_by_type(HttpClient $client, Factory $factory, Response $response, Stream $body): void
{
$data = file_get_contents('spec/Packagist/Api/Fixture/all.json');
$response->getBody()->shouldBeCalled()->willReturn($data);
$response->getBody()->shouldBeCalled()->willReturn($body);
$body->getContents()->shouldBeCalled()->willReturn($data);

$client->request('GET', 'https://packagist.org/packages/list.json?type=library')
->shouldBeCalled()
Expand All @@ -132,10 +156,11 @@ public function it_filters_package_names_by_type(HttpClient $client, Factory $fa
$this->all(['type' => 'library']);
}

public function it_filters_package_names_by_vendor(HttpClient $client, Factory $factory, Response $response)
public function it_filters_package_names_by_vendor(HttpClient $client, Factory $factory, Response $response, Stream $body): void
{
$data = file_get_contents('spec/Packagist/Api/Fixture/all.json');
$response->getBody()->shouldBeCalled()->willReturn($data);
$response->getBody()->shouldBeCalled()->willReturn($body);
$body->getContents()->shouldBeCalled()->willReturn($data);

$client->request('GET', 'https://packagist.org/packages/list.json?vendor=sylius')
->shouldBeCalled()
Expand All @@ -145,4 +170,5 @@ public function it_filters_package_names_by_vendor(HttpClient $client, Factory $

$this->all(['vendor' => 'sylius']);
}

}
2 changes: 1 addition & 1 deletion spec/Packagist/Api/Fixture/get_composer.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions spec/Packagist/Api/Fixture/get_composer_lite.json

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions spec/Packagist/Api/Result/FactorySpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,22 @@ public function it_creates_composer_packages()
}
}

public function it_creates_composer_lite_packages(): void
{
$data = json_decode(file_get_contents('spec/Packagist/Api/Fixture/get_composer_lite.json'), true);

$results = $this->create($data);
$results->shouldHaveCount(1);
$results->shouldBeArray();
foreach ($results as $result) {
$result->shouldBeAnInstanceOf('Packagist\Api\Result\Package');

foreach ($result->getVersions() as $version) {
$version->shouldBeAnInstanceOf('Packagist\Api\Result\Version');
}
}
}

public function it_creates_package_names()
{
$data = json_decode(file_get_contents('spec/Packagist/Api/Fixture/all.json'), true);
Expand Down Expand Up @@ -115,4 +131,5 @@ public function getMatchers(): array
}
);
}

}
48 changes: 28 additions & 20 deletions src/Packagist/Api/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use GuzzleHttp\Client as HttpClient;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\GuzzleException;
use Packagist\Api\Result\Factory;
use Packagist\Api\Result\Package;
use Psr\Http\Message\StreamInterface;
Expand Down Expand Up @@ -55,9 +56,9 @@ public function __construct(
* * type: type of package (type in composer.json)
* * tags: tags of package (keywords in composer.json)
*
* @param string $query Name of package
* @param array $filters An array of filters
* @param int $limit Pages to limit results (0 = all pages)
* @param string $query Name of package
* @param array $filters An array of filters
* @param int $limit Pages to limit results (0 = all pages)
*
* @return array The results
*/
Expand Down Expand Up @@ -98,23 +99,25 @@ public function get(string $package)
}

/**
* Similar to {@link get()}, but uses composer metadata which is Packagist's preferred
* way of retrieving details, since responses are cached efficiently as static files
* by the Packagist service. The response lacks some metadata that is provided
* by {@link get()}, see https://packagist.org/apidoc for details.
*
* Caution: Returns an array of packages, you need to select the correct one
* from the indexed array.
*
* @since 1.6
*
* @see https://packagist.org/apidoc#get-package-data
* Contains tagged releases and dev versions
* @param string $package Full qualified name ex : myname/mypackage
*
* @return \Packagist\Api\Result\Package[] An array of packages, including the requested one.
* @return Package[] An array of packages, including the requested one.
*/
public function getComposer(string $package): array
{
return $this->respond(sprintf($this->url('/p2/%s~dev.json'), $package));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be careful. The ~dev.json files is a not about getting all versions. It is about getting dev versions. None of the releases (be them stable releases or beta releases) are in that file. If you want to get all versions, you need to load both files.

}

/**
* @see https://packagist.org/apidoc#get-package-data
* Contains only tagged releases
* @param string $package Full qualified name ex : myname/mypackage
* @return Package[] An array of packages, including the requested one.
*/
public function getComposer($package)
public function getComposerLite(string $package): array
{
return $this->respond(sprintf($this->url('/p/%s.json'), $package));
return $this->respond(sprintf($this->url('/p2/%s.json'), $package));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just confirming that the change of the API we consume in this method was deliberate? I have no problem with changing the underlying API we consume from v1 to v2. Is the intention of adding getComposerLite() to allow access to the original v1 API endpoint, or to return the same results as getComposer() except without dev dependencies?

Copy link
Contributor Author

@JellyBellyDev JellyBellyDev Dec 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! I confirm!
I add getComposerLite() to return the same results of getComposer() except without dev dependencies as descripted here in the Getting package data section.

}

/**
Expand Down Expand Up @@ -197,9 +200,14 @@ protected function respond(string $url)
*/
protected function request(string $url): string
{
return (string) $this->httpClient
->request('GET', $url)
->getBody();
try {
return $this->httpClient
->request('GET', $url)
->getBody()
->getContents();
} catch (GuzzleException $e) {
return json_encode([]);
}
}

/**
Expand Down
40 changes: 40 additions & 0 deletions src/Packagist/Api/Result/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ public function create(array $data)
// Used for /explore/popular.json
return $this->createSearchResults($data['packages']);
}
if (isset($data['minified']) && 'composer/2.0' === $data['minified']) {
// Used for /p2/<package>.json
return $this->createComposer2PackagesResults($data['packages']);
}
// Used for /p/<package>.json
return $this->createComposerPackagesResults($data['packages']);
}
Expand Down Expand Up @@ -127,6 +131,19 @@ public function createPackageResults(array $package): Package
$version['name'] ??= '';
$version['type'] ??= '';

if (isset($version['conflict']) && !is_array($version['conflict'])) {
unset($version['conflict']);
}
if (isset($version['suggest']) && !is_array($version['suggest'])) {
unset($version['suggest']);
}
if (isset($version['replace']) && !is_array($version['replace'])) {
unset($version['replace']);
}
if (isset($version['provide']) && !is_array($version['provide'])) {
unset($version['provide']);
}

if (isset($version['authors']) && $version['authors']) {
foreach ($version['authors'] as $key => $author) {
// Cast some potentially null properties to empty strings
Expand Down Expand Up @@ -171,4 +188,27 @@ protected function createResult(string $class, array $data): AbstractResult

return $result;
}

/**
* @param array $packages
* @return Package[]
*/
public function createComposer2PackagesResults(array $packages): array
{
$created = [];

foreach ($packages as $name => $package) {
// Create an empty package, only contains versions
$createdPackage = array(
'versions' => [],
);
foreach ($package as $version) {
$createdPackage['versions'][$version['version']] = $version;
}

$created[$name] = $this->createPackageResults($createdPackage);
}

return $created;
}
}