Skip to content

Commit 7598e8b

Browse files
committed
first commit
0 parents  commit 7598e8b

File tree

11 files changed

+1276
-0
lines changed

11 files changed

+1276
-0
lines changed

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
vendor/
2+
composer.lock
3+
phpunit.xml
4+
.DS_Store
5+
*/.DS_Store
6+
*/*/.DS_Store
7+
*/*/*/.DS_Store
8+
.phplint-cache

LICENSE

Lines changed: 674 additions & 0 deletions
Large diffs are not rendered by default.

composer.json

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"name": "dmitryrechkin/php-graphql-schema-fileparser",
3+
"type": "php-graphql-schema-fileparser",
4+
"description": "PHP GraphQL Schema file parser with caching",
5+
"keywords": [
6+
],
7+
"license": "AGPL-3.0-only",
8+
"authors": [
9+
{
10+
"name": "Dmitry Rechkin",
11+
"email": "[email protected]",
12+
"homepage": "https://github.com/dmitryrechkin/"
13+
}
14+
],
15+
"minimum-stability": "dev",
16+
"require": {
17+
"php": "^7.3 || ^8.0",
18+
"webonyx/graphql-php": "^14.11"
19+
},
20+
"require-dev": {
21+
"dmitryrechkin/php-standard": "^1.0",
22+
"dmitryrechkin/php-phpunit-bootstrap": "^1.0"
23+
},
24+
"autoload": {
25+
"psr-4": {
26+
"DmitryRechkin\\" : "src/"
27+
}
28+
},
29+
"autoload-dev": {
30+
"psr-4": {
31+
"DmitryRechkin\\Tests\\" : "tests/"
32+
}
33+
},
34+
"config": {
35+
"allow-plugins": {
36+
"dealerdirect/phpcodesniffer-composer-installer": true
37+
}
38+
}
39+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DmitryRechkin\GraphQL\Schema\FileParser;
6+
7+
class CacheFilePathBuilder
8+
{
9+
/**
10+
* @var string
11+
*/
12+
private $targetDirectoryPath;
13+
14+
/**
15+
* @var string
16+
*/
17+
private $sourceFilePath;
18+
19+
/**
20+
* constructor
21+
*/
22+
public function __construct()
23+
{
24+
$this->targetDirectoryPath = sys_get_temp_dir();
25+
$this->sourceFilePath = '';
26+
}
27+
28+
/**
29+
* sets target directory where cache file should be located
30+
*
31+
* @param string $targetDirectoryPath
32+
* @return self
33+
*/
34+
public function withTargetDirectoryPath(string $targetDirectoryPath): self
35+
{
36+
$this->targetDirectoryPath = rtrim($targetDirectoryPath, PATH_SEPARATOR);
37+
return $this;
38+
}
39+
40+
/**
41+
* sets source file path
42+
*
43+
* @param string $sourceFilePath
44+
* @return self
45+
*/
46+
public function withSourceFilePath(string $sourceFilePath): self
47+
{
48+
$this->sourceFilePath = rtrim($sourceFilePath, PATH_SEPARATOR);
49+
return $this;
50+
}
51+
52+
/**
53+
* returns path to a cache file
54+
*
55+
* @return string
56+
*/
57+
public function build(): string
58+
{
59+
return $this->targetDirectoryPath . '/' . md5($this->sourceFilePath);
60+
}
61+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DmitryRechkin\GraphQL\Schema\FileParser;
6+
7+
use GraphQL\Language\AST\DocumentNode;
8+
use GraphQL\Utils\AST;
9+
10+
class DocumentNodeStorage
11+
{
12+
/**
13+
* loads a document node from a give file
14+
*
15+
* @param string $filePath
16+
* @return DocumentNode
17+
*/
18+
public function load(string $filePath): ?DocumentNode
19+
{
20+
if (false === file_exists($filePath)) {
21+
return null;
22+
}
23+
24+
return AST::fromArray(require $filePath);
25+
}
26+
27+
/**
28+
* saves to a given file a given document node
29+
*
30+
* @param string $filePath
31+
* @param DocumentNode $documentNode
32+
* @return boolean
33+
*/
34+
public function save(string $filePath, DocumentNode $documentNode): bool
35+
{
36+
$contents = "<?php\nreturn " . var_export(AST::toArray($documentNode), true) . ";\n";
37+
38+
return false !== file_put_contents($filePath, $contents);
39+
}
40+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DmitryRechkin\GraphQL\Schema\FileParser;
6+
7+
class FileModificationTimeComparator
8+
{
9+
/**
10+
* @var string
11+
*/
12+
private $filePath;
13+
14+
/**
15+
* constructor
16+
*/
17+
public function __construct()
18+
{
19+
$this->filePath = '';
20+
}
21+
22+
/**
23+
* sets file we will be comparing against
24+
*
25+
* @param string $filePath
26+
* @return self
27+
*/
28+
public function forFile(string $filePath): self
29+
{
30+
$this->filePath = $filePath;
31+
return $this;
32+
}
33+
34+
/**
35+
* returns true when our file is newer than a given file
36+
*
37+
* @param string $compareToFilePath
38+
* @return boolean
39+
*/
40+
public function isNewerThanFile(string $compareToFilePath): bool
41+
{
42+
if (false === file_exists($this->filePath)) {
43+
return false;
44+
}
45+
46+
if (false === file_exists($compareToFilePath)) {
47+
return true;
48+
}
49+
50+
return filemtime($this->filePath) > filemtime($compareToFilePath);
51+
}
52+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DmitryRechkin\GraphQL\Schema\FileParser;
6+
7+
use GraphQL\Language\AST\DocumentNode;
8+
use GraphQL\Language\Parser as LanguageParser;
9+
10+
class FileParser
11+
{
12+
/**
13+
* @var CacheFilePathBuilder
14+
*/
15+
private $cacheFilePathBuilder;
16+
17+
/**
18+
* @var FileModificationTimeComparator
19+
*/
20+
private $fileModificationTimeComparator;
21+
22+
/**
23+
* @var DocumentNodeStorage
24+
*/
25+
private $documentNodeStorage;
26+
27+
/**
28+
* constructor
29+
*
30+
* @param CacheFilePathBuilder $cacheFilePathBuilder
31+
* @param FileModificationTimeComparator $fileModificationTimeComparator
32+
* @param DocumentNodeStorage $documentNodeStorage
33+
*/
34+
public function __construct(
35+
CacheFilePathBuilder $cacheFilePathBuilder = null,
36+
FileModificationTimeComparator $fileModificationTimeComparator = null,
37+
DocumentNodeStorage $documentNodeStorage = null
38+
) {
39+
$this->cacheFilePathBuilder = $cacheFilePathBuilder ?? new CacheFilePathBuilder();
40+
$this->fileModificationTimeComparator = $fileModificationTimeComparator ?? new FileModificationTimeComparator();
41+
$this->documentNodeStorage = $documentNodeStorage ?? new DocumentNodeStorage();
42+
}
43+
44+
/**
45+
* parses a given GraphQL schema file returns and DocumentNode,
46+
* or loads and returns previously parsed and then cached DocumentNode
47+
*
48+
* @param string $filePath
49+
* @return DocumentNode
50+
*/
51+
public function parse(string $filePath): ?DocumentNode
52+
{
53+
if (false === file_exists($filePath)) {
54+
return null;
55+
}
56+
57+
$documentNode = null;
58+
$cacheFilePath = $this->cacheFilePathBuilder->withSourceFilePath($filePath)->build();
59+
60+
if ($this->fileModificationTimeComparator->forFile($cacheFilePath)->isNewerThanFile($filePath)) {
61+
$documentNode = $this->documentNodeStorage->load($cacheFilePath);
62+
} else {
63+
$documentNode = LanguageParser::parse(file_get_contents($filePath));
64+
65+
$this->documentNodeStorage->save($cacheFilePath, $documentNode);
66+
}
67+
68+
return $documentNode;
69+
}
70+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DmitryRechkin\Tests\Unit\GraphQL\Schema\FileParser;
6+
7+
use DmitryRechkin\GraphQL\Schema\FileParser\CacheFilePathBuilder;
8+
use PHPUnit\Framework\TestCase;
9+
10+
class CacheFilePathBuilderTest extends TestCase
11+
{
12+
/**
13+
* @return void
14+
*/
15+
public function testWithTargetDirectoryReturnsSelf(): void
16+
{
17+
$cacheFilePathBuilder = new CacheFilePathBuilder();
18+
$this->assertSame($cacheFilePathBuilder, $cacheFilePathBuilder->withTargetDirectoryPath(__DIR__));
19+
}
20+
21+
/**
22+
* @return void
23+
*/
24+
public function testWithSourceFilePathReturnsSelf(): void
25+
{
26+
$cacheFilePathBuilder = new CacheFilePathBuilder();
27+
$this->assertSame($cacheFilePathBuilder, $cacheFilePathBuilder->withSourceFilePath('somefile.php'));
28+
}
29+
30+
/**
31+
* @return void
32+
*/
33+
public function testBuildReturnsExpectedPath(): void
34+
{
35+
$targetDirectoryPath = __DIR__;
36+
$sourceFilePath = __DIR__ . '/test.php';
37+
$expectedPath = $targetDirectoryPath . '/' . md5($sourceFilePath);
38+
39+
$cacheFilePathBuilder = new CacheFilePathBuilder();
40+
$cacheFilePathBuilder->withTargetDirectoryPath($targetDirectoryPath);
41+
$cacheFilePathBuilder->withSourceFilePath($sourceFilePath);
42+
43+
$this->assertSame($expectedPath, $cacheFilePathBuilder->build());
44+
}
45+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DmitryRechkin\Tests\Unit\GraphQL\Schema\FileParser;
6+
7+
use DmitryRechkin\GraphQL\Schema\FileParser\DocumentNodeStorage;
8+
use GraphQL\Language\AST\DocumentNode;
9+
use PHPUnit\Framework\TestCase;
10+
11+
class DocumentNodeStorageTest extends TestCase
12+
{
13+
/**
14+
* @return void
15+
*/
16+
public function testSaveFollowedByLoadReturnsDocumentNode(): void
17+
{
18+
$filePath = sys_get_temp_dir() . '/document-node-storage-test.php';
19+
20+
$documentNodeStorage = new DocumentNodeStorage();
21+
$this->assertTrue($documentNodeStorage->save($filePath, new DocumentNode([])));
22+
$this->assertInstanceOf(DocumentNode::class, $documentNodeStorage->load($filePath));
23+
24+
unlink($filePath);
25+
}
26+
}

0 commit comments

Comments
 (0)