Skip to content

Commit df2996e

Browse files
committed
[JsonStreamer] Rename the component
0 parents  commit df2996e

File tree

171 files changed

+8217
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

171 files changed

+8217
-0
lines changed

.gitattributes

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/Tests export-ignore
2+
/phpunit.xml.dist export-ignore
3+
/.gitattributes export-ignore
4+
/.gitignore export-ignore

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
vendor/
2+
composer.lock
3+
phpunit.xml

Attribute/JsonStreamable.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\JsonStreamer\Attribute;
13+
14+
/**
15+
* @author Mathias Arlaud <[email protected]>
16+
*
17+
* @experimental
18+
*/
19+
#[\Attribute(\Attribute::TARGET_CLASS)]
20+
final class JsonStreamable
21+
{
22+
public function __construct(
23+
public bool $asObject = true,
24+
public bool $asList = true,
25+
) {
26+
}
27+
}

Attribute/StreamedName.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\JsonStreamer\Attribute;
13+
14+
/**
15+
* Defines the streamed property name.
16+
*
17+
* @author Mathias Arlaud <[email protected]>
18+
*
19+
* @experimental
20+
*/
21+
#[\Attribute(\Attribute::TARGET_PROPERTY)]
22+
final class StreamedName
23+
{
24+
public function __construct(
25+
private string $name,
26+
) {
27+
}
28+
29+
public function getName(): string
30+
{
31+
return $this->name;
32+
}
33+
}

Attribute/ValueTransformer.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\JsonStreamer\Attribute;
13+
14+
use Symfony\Component\JsonStreamer\Exception\LogicException;
15+
16+
/**
17+
* Defines a callable or a {@see \Symfony\Component\JsonStreamer\ValueTransformer\ValueTransformerInterface} service id
18+
* that will be used to transform the property data during stream reading/writing.
19+
*
20+
* @author Mathias Arlaud <[email protected]>
21+
*
22+
* @experimental
23+
*/
24+
#[\Attribute(\Attribute::TARGET_PROPERTY)]
25+
class ValueTransformer
26+
{
27+
private \Closure|string|null $streamToNative;
28+
private \Closure|string|null $nativeToStream;
29+
30+
/**
31+
* @param (callable(mixed, array<string, mixed>=): mixed)|string|null $streamToNative
32+
* @param (callable(mixed, array<string, mixed>=): mixed)|string|null $nativeToStream
33+
*/
34+
public function __construct(
35+
callable|string|null $streamToNative = null,
36+
callable|string|null $nativeToStream = null,
37+
) {
38+
if (!$streamToNative && !$nativeToStream) {
39+
throw new LogicException('#[ValueTransformer] attribute must declare either $streamToNative or $nativeToStream.');
40+
}
41+
42+
if (\is_callable($streamToNative)) {
43+
$streamToNative = $streamToNative(...);
44+
}
45+
46+
if (\is_callable($nativeToStream)) {
47+
$nativeToStream = $nativeToStream(...);
48+
}
49+
50+
$this->streamToNative = $streamToNative;
51+
$this->nativeToStream = $nativeToStream;
52+
}
53+
54+
public function getStreamToNative(): string|\Closure|null
55+
{
56+
return $this->streamToNative;
57+
}
58+
59+
public function getNativeToStream(): string|\Closure|null
60+
{
61+
return $this->nativeToStream;
62+
}
63+
}

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CHANGELOG
2+
=========
3+
4+
7.3
5+
---
6+
7+
* Introduce the component as experimental

CacheWarmer/LazyGhostCacheWarmer.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\JsonStreamer\CacheWarmer;
13+
14+
use Symfony\Component\Filesystem\Filesystem;
15+
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmer;
16+
use Symfony\Component\JsonStreamer\Exception\RuntimeException;
17+
use Symfony\Component\VarExporter\ProxyHelper;
18+
19+
/**
20+
* Generates lazy ghost {@see \Symfony\Component\VarExporter\LazyGhostTrait}
21+
* PHP files for $streamable types.
22+
*
23+
* @author Mathias Arlaud <[email protected]>
24+
*
25+
* @internal
26+
*/
27+
final class LazyGhostCacheWarmer extends CacheWarmer
28+
{
29+
private Filesystem $fs;
30+
31+
/**
32+
* @param iterable<class-string> $streamableClassNames
33+
*/
34+
public function __construct(
35+
private iterable $streamableClassNames,
36+
private string $lazyGhostsDir,
37+
) {
38+
$this->fs = new Filesystem();
39+
}
40+
41+
public function warmUp(string $cacheDir, ?string $buildDir = null): array
42+
{
43+
if (!$this->fs->exists($this->lazyGhostsDir)) {
44+
$this->fs->mkdir($this->lazyGhostsDir);
45+
}
46+
47+
foreach ($this->streamableClassNames as $className) {
48+
$this->warmClassLazyGhost($className);
49+
}
50+
51+
return [];
52+
}
53+
54+
public function isOptional(): bool
55+
{
56+
return true;
57+
}
58+
59+
/**
60+
* @param class-string $className
61+
*/
62+
private function warmClassLazyGhost(string $className): void
63+
{
64+
$path = \sprintf('%s%s%s.php', $this->lazyGhostsDir, \DIRECTORY_SEPARATOR, hash('xxh128', $className));
65+
66+
try {
67+
$classReflection = new \ReflectionClass($className);
68+
} catch (\ReflectionException $e) {
69+
throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
70+
}
71+
72+
$this->writeCacheFile($path, \sprintf(
73+
'class %s%s',
74+
\sprintf('%sGhost', preg_replace('/\\\\/', '', $className)),
75+
ProxyHelper::generateLazyGhost($classReflection),
76+
));
77+
}
78+
}

CacheWarmer/StreamerCacheWarmer.php

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\JsonStreamer\CacheWarmer;
13+
14+
use Psr\Log\LoggerInterface;
15+
use Psr\Log\NullLogger;
16+
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
17+
use Symfony\Component\JsonStreamer\Exception\ExceptionInterface;
18+
use Symfony\Component\JsonStreamer\Mapping\PropertyMetadataLoaderInterface;
19+
use Symfony\Component\JsonStreamer\Read\StreamReaderGenerator;
20+
use Symfony\Component\JsonStreamer\Write\StreamWriterGenerator;
21+
use Symfony\Component\TypeInfo\Type;
22+
23+
/**
24+
* Generates stream readers and stream writers PHP files.
25+
*
26+
* @author Mathias Arlaud <[email protected]>
27+
*
28+
* @internal
29+
*/
30+
final class StreamerCacheWarmer implements CacheWarmerInterface
31+
{
32+
private StreamWriterGenerator $streamWriterGenerator;
33+
private StreamReaderGenerator $streamReaderGenerator;
34+
35+
/**
36+
* @param iterable<class-string, array{object: bool, list: bool}> $streamable
37+
*/
38+
public function __construct(
39+
private iterable $streamable,
40+
PropertyMetadataLoaderInterface $streamWriterPropertyMetadataLoader,
41+
PropertyMetadataLoaderInterface $streamReaderPropertyMetadataLoader,
42+
string $streamWritersDir,
43+
string $streamReadersDir,
44+
private LoggerInterface $logger = new NullLogger(),
45+
) {
46+
$this->streamWriterGenerator = new StreamWriterGenerator($streamWriterPropertyMetadataLoader, $streamWritersDir);
47+
$this->streamReaderGenerator = new StreamReaderGenerator($streamReaderPropertyMetadataLoader, $streamReadersDir);
48+
}
49+
50+
public function warmUp(string $cacheDir, ?string $buildDir = null): array
51+
{
52+
foreach ($this->streamable as $className => $streamable) {
53+
if ($streamable['object']) {
54+
$type = Type::object($className);
55+
56+
$this->warmUpStreamWriter($type);
57+
$this->warmUpStreamReaders($type);
58+
}
59+
60+
if ($streamable['list']) {
61+
$type = Type::list(Type::object($className));
62+
63+
$this->warmUpStreamWriter($type);
64+
$this->warmUpStreamReaders($type);
65+
}
66+
}
67+
68+
return [];
69+
}
70+
71+
public function isOptional(): bool
72+
{
73+
return true;
74+
}
75+
76+
private function warmUpStreamWriter(Type $type): void
77+
{
78+
try {
79+
$this->streamWriterGenerator->generate($type);
80+
} catch (ExceptionInterface $e) {
81+
$this->logger->debug('Cannot generate "json" stream writer for "{type}": {exception}', ['type' => (string) $type, 'exception' => $e]);
82+
}
83+
}
84+
85+
private function warmUpStreamReaders(Type $type): void
86+
{
87+
try {
88+
$this->streamReaderGenerator->generate($type, false);
89+
} catch (ExceptionInterface $e) {
90+
$this->logger->debug('Cannot generate "json" string stream reader for "{type}": {exception}', ['type' => (string) $type, 'exception' => $e]);
91+
}
92+
93+
try {
94+
$this->streamReaderGenerator->generate($type, true);
95+
} catch (ExceptionInterface $e) {
96+
$this->logger->debug('Cannot generate "json" resource stream reader for "{type}": {exception}', ['type' => (string) $type, 'exception' => $e]);
97+
}
98+
}
99+
}

DataModel/DataAccessorInterface.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\JsonStreamer\DataModel;
13+
14+
use PhpParser\Node\Expr;
15+
16+
/**
17+
* Represents a way to access data on PHP.
18+
*
19+
* @author Mathias Arlaud <[email protected]>
20+
*
21+
* @internal
22+
*/
23+
interface DataAccessorInterface
24+
{
25+
/**
26+
* Converts to "nikic/php-parser" PHP expression.
27+
*/
28+
public function toPhpExpr(): Expr;
29+
}

DataModel/FunctionDataAccessor.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\JsonStreamer\DataModel;
13+
14+
use PhpParser\BuilderFactory;
15+
use PhpParser\Node\Expr;
16+
17+
/**
18+
* Defines the way to access data using a function (or a method).
19+
*
20+
* @author Mathias Arlaud <[email protected]>
21+
*
22+
* @internal
23+
*/
24+
final class FunctionDataAccessor implements DataAccessorInterface
25+
{
26+
/**
27+
* @param list<DataAccessorInterface> $arguments
28+
*/
29+
public function __construct(
30+
private string $functionName,
31+
private array $arguments,
32+
private ?DataAccessorInterface $objectAccessor = null,
33+
) {
34+
}
35+
36+
public function toPhpExpr(): Expr
37+
{
38+
$builder = new BuilderFactory();
39+
$arguments = array_map(static fn (DataAccessorInterface $argument): Expr => $argument->toPhpExpr(), $this->arguments);
40+
41+
if (null === $this->objectAccessor) {
42+
return $builder->funcCall($this->functionName, $arguments);
43+
}
44+
45+
return $builder->methodCall($this->objectAccessor->toPhpExpr(), $this->functionName, $arguments);
46+
}
47+
}

0 commit comments

Comments
 (0)