Skip to content

Commit

Permalink
Merge pull request #3487 from phpDocumentor/cache-per-documentation-set
Browse files Browse the repository at this point in the history
Cache per documentation set
  • Loading branch information
jaapio committed Mar 23, 2023
2 parents 7068b5c + a23706d commit 4247244
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 75 deletions.
132 changes: 89 additions & 43 deletions src/phpDocumentor/Descriptor/Cache/ProjectDescriptorMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,18 @@

namespace phpDocumentor\Descriptor\Cache;

use phpDocumentor\Descriptor\ApiSetDescriptor;
use phpDocumentor\Descriptor\FileDescriptor;
use phpDocumentor\Descriptor\ProjectDescriptor;
use phpDocumentor\Descriptor\VersionDescriptor;
use phpDocumentor\Reflection\File;
use Psr\Cache\CacheItemInterface;
use Symfony\Component\Cache\Adapter\AdapterInterface;

use function array_diff;
use function array_map;
use function md5;
use function sprintf;

/**
* Maps a projectDescriptor to and from a cache instance.
Expand Down Expand Up @@ -51,7 +54,73 @@ public function populate(ProjectDescriptor $projectDescriptor): void
{
$this->loadCacheItemAsSettings($projectDescriptor);

$fileList = $this->cache->getItem(self::FILE_LIST)->get();
foreach ($projectDescriptor->getVersions() as $version) {
$this->populateVersion($version);
}
}

/**
* Stores a Project Descriptor in the Cache.
*/
public function save(ProjectDescriptor $projectDescriptor): void
{
// store the settings for this Project Descriptor
$item = $this->cache->getItem(self::KEY_SETTINGS);
$this->cache->saveDeferred($item->set($projectDescriptor->getSettings()));

foreach ($projectDescriptor->getVersions() as $version) {
$this->saveVersion($version);
}
}

/**
* Removes all files in cache that do not occur in the given FileSet Collection.
*
* @param File[] $files
*/
public function garbageCollect(VersionDescriptor $version, ApiSetDescriptor $apiSet, array $files): void
{
$fileListKey = $this->getApiSetFileListCacheKey($version, $apiSet);
$fileListItem = $this->cache->getItem($fileListKey);
$cachedFileList = $fileListItem->get();

if ($cachedFileList === null) {
return;
}

$realFileKeys = array_map(
fn (File $file) => $this->getApiSetFileKey($fileListKey, $file->path()),
$files
);

$this->cache->deleteItems(array_diff($cachedFileList, $realFileKeys));
}

private function loadCacheItemAsSettings(ProjectDescriptor $projectDescriptor): void
{
$item = $this->cache->getItem(self::KEY_SETTINGS);
if (!$item->isHit()) {
return;
}

$settings = $item->get();
$projectDescriptor->setSettings($settings);
}

private function populateVersion(VersionDescriptor $version): void
{
/** @var ApiSetDescriptor[] $apiSets */
$apiSets = $version->getDocumentationSets()->filter(ApiSetDescriptor::class);

foreach ($apiSets as $apiSet) {
$this->populateApiSet($version, $apiSet);
}
}

private function populateApiSet(VersionDescriptor $version, ApiSetDescriptor $apiSet): void
{
$key = $this->getApiSetFileListCacheKey($version, $apiSet);
$fileList = $this->cache->getItem($key)->get();
if ($fileList === null) {
return;
}
Expand All @@ -64,28 +133,30 @@ public function populate(ProjectDescriptor $projectDescriptor): void
continue;
}

$projectDescriptor->getFiles()->set($file->getPath(), $file);
$apiSet->getFiles()->set($file->getPath(), $file);
}
}

/**
* Stores a Project Descriptor in the Cache.
*/
public function save(ProjectDescriptor $projectDescriptor): void
private function saveVersion(VersionDescriptor $version): void
{
$fileListItem = $this->cache->getItem(self::FILE_LIST);
$currentFileList = $fileListItem->get();
$apiSets = $version->getDocumentationSets()->filter(ApiSetDescriptor::class);
foreach ($apiSets as $apiSet) {
$this->saveApiSet($version, $apiSet);
}
}

// store the settings for this Project Descriptor
$item = $this->cache->getItem(self::KEY_SETTINGS);
$this->cache->saveDeferred($item->set($projectDescriptor->getSettings()));
private function saveApiSet(VersionDescriptor $version, ApiSetDescriptor $apiSet): void
{
$fileListKey = $this->getApiSetFileListCacheKey($version, $apiSet);
$fileListItem = $this->cache->getItem($fileListKey);
$currentFileList = $fileListItem->get();

// store cache items
$fileKeys = [];
foreach ($projectDescriptor->getFiles() as $file) {
$key = self::FILE_PREFIX . md5($file->getPath());
foreach ($apiSet->getFiles() as $file) {
$key = $this->getApiSetFileKey($fileListKey, $file->getPath());
$fileKeys[] = $key;
$item = $this->cache->getItem($key);
$item = $this->cache->getItem($key);
$this->cache->saveDeferred($item->set($file));
}

Expand All @@ -105,38 +176,13 @@ public function save(ProjectDescriptor $projectDescriptor): void
$this->cache->deleteItems($invalidatedKeys);
}

/**
* Removes all files in cache that do not occur in the given FileSet Collection.
*
* @param File[] $files
*/
public function garbageCollect(array $files): void
private function getApiSetFileListCacheKey(VersionDescriptor $version, ApiSetDescriptor $apiSet): string
{
$fileListItem = $this->cache->getItem(self::FILE_LIST);
$cachedFileList = $fileListItem->get();

if ($cachedFileList === null) {
return;
}

$realFileKeys = array_map(
static function (File $file) {
return self::FILE_PREFIX . md5($file->path());
},
$files
);

$this->cache->deleteItems(array_diff($cachedFileList, $realFileKeys));
return sprintf('%s-%s-%s', self::FILE_LIST, $version->getNumber(), $apiSet->getName());
}

private function loadCacheItemAsSettings(ProjectDescriptor $projectDescriptor): void
private function getApiSetFileKey(string $fileListKey, string $path): string
{
$item = $this->cache->getItem(self::KEY_SETTINGS);
if (!$item->isHit()) {
return;
}

$settings = $item->get();
$projectDescriptor->setSettings($settings);
return sprintf('%s%s', self::FILE_PREFIX, md5($fileListKey . $path));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ public function __construct(ProjectDescriptorMapper $descriptorMapper)

public function __invoke(ApiSetPayload $payload): ApiSetPayload
{
$this->descriptorMapper->garbageCollect($payload->getFiles());
$this->descriptorMapper->garbageCollect(
$payload->getVersion(),
$payload->getApiSet(),
$payload->getFiles()
);

return $payload;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function __invoke(Payload $payload): Payload
foreach ($versions as $version) {
foreach ($version->getDocumentationSets()->filter(ApiSetDescriptor::class) as $apiSet) {
$this->parseApiDocumentationSetPipeline->process(
new Parser\ApiSetPayload($payload->getConfig(), $payload->getBuilder(), $apiSet)
new Parser\ApiSetPayload($payload->getConfig(), $payload->getBuilder(), $version, $apiSet)
);
}
}
Expand Down
16 changes: 13 additions & 3 deletions src/phpDocumentor/Pipeline/Stage/Parser/ApiSetPayload.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use phpDocumentor\Configuration\Configuration;
use phpDocumentor\Descriptor\ApiSetDescriptor;
use phpDocumentor\Descriptor\ProjectDescriptorBuilder;
use phpDocumentor\Descriptor\VersionDescriptor;
use phpDocumentor\Reflection\File;

use function array_merge;
Expand All @@ -25,11 +26,13 @@
*/
final class ApiSetPayload
{
/** @var ConfigurationMap */
private array $configuration;
private ProjectDescriptorBuilder $builder;
private VersionDescriptor $version;
private ApiSetDescriptor $apiSet;

/** @var ConfigurationMap */
private array $configuration;

/** @var File[] */
private array $files;

Expand All @@ -40,13 +43,14 @@ final class ApiSetPayload
public function __construct(
array $configuration,
ProjectDescriptorBuilder $builder,
VersionDescriptor $version,
ApiSetDescriptor $apiSet,
array $files = []
) {
$this->configuration = $configuration;
$this->builder = $builder;
$this->version = $version;
$this->apiSet = $apiSet;

$this->files = $files;
}

Expand All @@ -63,6 +67,11 @@ public function getBuilder(): ProjectDescriptorBuilder
return $this->builder;
}

public function getVersion(): VersionDescriptor
{
return $this->version;
}

public function getApiSet(): ApiSetDescriptor
{
return $this->apiSet;
Expand All @@ -76,6 +85,7 @@ public function withFiles(array $files): self
return new static(
$this->getConfiguration(),
$this->getBuilder(),
$this->getVersion(),
$this->getApiSet(),
array_merge($this->getFiles(), $files)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,17 @@ protected function setUp(): void
*/
public function testThatATheSettingsForAProjectDescriptorArePersistedAndCanBeRetrievedFromCache(): void
{
$versionNumber = $this->faker()->numerify('v#.#.#');
$apiSetName = $this->faker()->word;

$fileDescriptor = new FileDescriptor('fileHash');
$fileDescriptor->setPath('./src/MyClass.php');

$projectDescriptor = new ProjectDescriptor('project');
$set = $this->faker()->apiSetDescriptor();
$version = $this->faker()->versionDescriptor([$set]);
$projectDescriptor->getVersions()->add($version);
$projectDescriptor->getFiles()->set('./src/MyClass.php', $fileDescriptor);
$restoredSet = $this->faker()->apiSetDescriptor($apiSetName);
$restoredVersion = $this->faker()->versionDescriptor([$restoredSet], $versionNumber);
$projectDescriptor->getVersions()->add($restoredVersion);
$restoredSet->getFiles()->set('./src/MyClass.php', $fileDescriptor);

$this->assertFalse($projectDescriptor->getSettings()->shouldIncludeSource());
$projectDescriptor->getSettings()->includeSource();
Expand All @@ -65,12 +68,12 @@ public function testThatATheSettingsForAProjectDescriptorArePersistedAndCanBeRet
$this->mapper->save($projectDescriptor);

$restoredProjectDescriptor = new ProjectDescriptor('project2');
$set = $this->faker()->apiSetDescriptor();
$version = $this->faker()->versionDescriptor([$set]);
$restoredProjectDescriptor->getVersions()->add($version);
$restoredSet = $this->faker()->apiSetDescriptor($apiSetName);
$restoredVersion = $this->faker()->versionDescriptor([$restoredSet], $versionNumber);
$restoredProjectDescriptor->getVersions()->add($restoredVersion);
$this->mapper->populate($restoredProjectDescriptor);

$this->assertTrue($restoredProjectDescriptor->getSettings()->shouldIncludeSource());
$this->assertEquals($fileDescriptor, $restoredProjectDescriptor->getFiles()->get($fileDescriptor->getPath()));
$this->assertEquals($fileDescriptor, $restoredSet->getFiles()->get($fileDescriptor->getPath()));
}
}
8 changes: 4 additions & 4 deletions tests/unit/phpDocumentor/Faker/Provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,18 +136,18 @@ public function fileDescriptor(): FileDescriptor
}

/** @param DocumentationSetDescriptor[] $documentationSets */
public function versionDescriptor(array $documentationSets): VersionDescriptor
public function versionDescriptor(array $documentationSets, ?string $version = null): VersionDescriptor
{
return new VersionDescriptor(
$this->generator->numerify('v#.#.#'),
$version ?? $this->generator->numerify('v#.#.#'),
DescriptorCollection::fromClassString(DocumentationSetDescriptor::class, $documentationSets)
);
}

public function apiSetDescriptor(): ApiSetDescriptor
public function apiSetDescriptor(?string $name = null): ApiSetDescriptor
{
return new ApiSetDescriptor(
$this->generator->word(),
$name ?? $this->generator->word(),
$this->source(),
(string) $this->path(),
$this->apiSpecification()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,15 @@ public function testItWillInstructTheDescriptorMapperToCollectGarbage(): void
$files = ['file1'];

$descriptorMapper = $this->prophesize(ProjectDescriptorMapper::class);
$descriptorMapper->garbageCollect($files)->shouldBeCalledOnce();

$fixture = new GarbageCollectCache($descriptorMapper->reveal());

$fixture(
new ApiSetPayload(
[],
$this->prophesize(ProjectDescriptorBuilder::class)->reveal(),
$this->faker()->apiSetDescriptor(),
$files
)
);
$builder = $this->prophesize(ProjectDescriptorBuilder::class)->reveal();
$apiSet = $this->faker()->apiSetDescriptor();
$version = $this->faker()->versionDescriptor([$apiSet]);

$descriptorMapper->garbageCollect($version, $apiSet, $files)->shouldBeCalledOnce();

$fixture(new ApiSetPayload([], $builder, $version, $apiSet, $files));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,15 @@ public function testFilesAreCollectedAndAddedToPayload(): void
null
);

$payload = new ApiSetPayload(
['phpdocumentor' => ['versions' => ['1.0.0' => $version]]],
$this->prophesize(ProjectDescriptorBuilder::class)->reveal(),
$this->faker()->apiSetDescriptor()
);
$config = ['phpdocumentor' => ['versions' => ['1.0.0' => $version]]];
$builder = $this->prophesize(ProjectDescriptorBuilder::class)->reveal();
$apiSet = $this->faker()->apiSetDescriptor();
$version = $this->faker()->versionDescriptor([$apiSet]);

$payload = new ApiSetPayload($config, $builder, $version, $apiSet);

$result = $fixture($payload);
self::assertEquals([], $result->getFiles());

self::assertSame([], $result->getFiles());
}
}

0 comments on commit 4247244

Please sign in to comment.