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
1 change: 1 addition & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ parameters:
- '/Method Sylius\\Bundle\\ResourceBundle\\Event\\ResourceControllerEvent::stop\(\) has no return typehint specified./'
- '/Method Sylius\\Bundle\\ResourceBundle\\Form\\Extension\\HttpFoundation\\HttpFoundationRequestHandler::handleRequest\(\) has no return typehint specified./'
- '/Method Sylius\\Bundle\\ResourceBundle\\Grid\\Controller\\ResourcesResolver::getResources\(\) has no return typehint specified./'
- '/Method Sylius\\Resource\\Metadata\\Extractor\\AbstractResourceExtractor::getResources\(\) should return array<Sylius\\Resource\\Metadata\\ResourceMetadata> but returns array<Sylius\\Resource\\Metadata\\ResourceMetadata>\|null./'
- '/Method Sylius\\Resource\\Model\\ResourceInterface::getId\(\) has no return typehint specified./'
- '/Method Sylius\\Resource\\Model\\TimestampableInterface::setCreatedAt\(\) has no return typehint specified./'
- '/Method Sylius\\Resource\\Model\\TimestampableInterface::setUpdatedAt\(\) has no return typehint specified./'
Expand Down
13 changes: 13 additions & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@
<InvalidReturnType>
<errorLevel type="suppress">
<file name="src/Component/src/Doctrine/Persistence/InMemoryRepository.php" />
<file name="src/Component/src/Metadata/Extractor/PhpFileResourceExtractor.php" />
</errorLevel>
</InvalidReturnType>

Expand Down Expand Up @@ -173,6 +174,12 @@
</errorLevel>
</MoreSpecificImplementedParamType>

<NullableReturnStatement>
<errorLevel type="suppress">
<file name="src/Component/src/Metadata/Extractor/PhpFileResourceExtractor.php" />
</errorLevel>
</NullableReturnStatement>

<NullArgument>
<errorLevel type="suppress">
<directory name="src" />
Expand Down Expand Up @@ -321,6 +328,12 @@
</errorLevel>
</UnrecognizedStatement>

<UnresolvableInclude>
<errorLevel type="suppress">
<file name="src/Component/src/Metadata/Extractor/PhpFileResourceExtractor.php" />
</errorLevel>
</UnresolvableInclude>

<UnsupportedReferenceUsage>
<errorLevel type="suppress">
<file name="src/Component/src/Doctrine/Persistence/InMemoryRepository.php" />
Expand Down
125 changes: 125 additions & 0 deletions src/Component/src/Metadata/Extractor/AbstractResourceExtractor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Resource\Metadata\Extractor;

use Psr\Container\ContainerInterface;
use Sylius\Resource\Exception\RuntimeException;
use Sylius\Resource\Metadata\ResourceMetadata;
use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface;

/**
* Base file extractor.
*
* @experimental
*/
abstract class AbstractResourceExtractor implements ResourceExtractorInterface
{
/** @var ResourceMetadata[]|null */
protected ?array $resources = null;

private array $collectedParameters = [];

/** @param string[] $paths */
public function __construct(
protected array $paths,
private readonly ?ContainerInterface $container = null,
) {
}

/**
* @inheritdoc
*/
Comment on lines +40 to +42
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/**
* @inheritdoc
*/

public function getResources(): array
{
if (null !== $this->resources) {
return $this->resources;
}

$this->resources = [];
foreach ($this->paths as $path) {
$this->extractFromPath($path);
}

return $this->resources;
}

/**
* Extracts metadata from a given path.
*/
abstract protected function extractFromPath(string $path): void;

/**
* Recursively replaces placeholders with the service container parameters.
*
* @see https://github.com/symfony/symfony/blob/6fec32c/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php
*
* @param mixed $value The source which might contain "%placeholders%"
*
* @throws \RuntimeException When a container value is not a string or a numeric value
*
* @return mixed The source with the placeholders replaced by the container
* parameters. Arrays are resolved recursively.
*/
protected function resolve(mixed $value): mixed
{
if (null === $this->container) {
return $value;
}

if (\is_array($value)) {
foreach ($value as $key => $val) {
$value[$key] = $this->resolve($val);
}

return $value;
}

if (!\is_string($value)) {
return $value;
}

$escapedValue = preg_replace_callback('/%%|%([^%\s]++)%/', function ($match) use ($value) {
$parameter = $match[1] ?? null;

// skip %%
if (!isset($parameter)) {
return '%%';
}

if (preg_match('/^env\(\w+\)$/', $parameter)) {
throw new RuntimeException(\sprintf('Using "%%%s%%" is not allowed in routing configuration.', $parameter));
}

if (\array_key_exists($parameter, $this->collectedParameters)) {
return $this->collectedParameters[$parameter];
}

if ($this->container instanceof SymfonyContainerInterface) {
$resolved = $this->container->getParameter($parameter);
} else {
$resolved = $this->container?->get($parameter);
}

if (\is_string($resolved) || is_numeric($resolved)) {
$this->collectedParameters[$parameter] = $resolved;

return (string) $resolved;
}

throw new RuntimeException(\sprintf('The container parameter "%s", used in the resource configuration value "%s", must be a string or numeric, but it is of type %s.', $parameter, $value, \gettype($resolved)));
}, $value);

return str_replace('%%', '%', $escapedValue ?? '');
}
}
53 changes: 53 additions & 0 deletions src/Component/src/Metadata/Extractor/PhpFileResourceExtractor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Resource\Metadata\Extractor;

use Sylius\Resource\Metadata\ResourceMetadata;

/**
* @experimental
*/
final class PhpFileResourceExtractor extends AbstractResourceExtractor
{
protected function extractFromPath(string $path): void
{
$resource = $this->getPHPFileClosure($path)();

if (!$resource instanceof ResourceMetadata) {
return;
}

$resourceReflection = new \ReflectionClass($resource);

foreach ($resourceReflection->getProperties() as $property) {
$property->setAccessible(true);
$resolvedValue = $this->resolve($property->getValue($resource));
$property->setValue($resource, $resolvedValue);
}

$this->resources[] = $resource;
}

/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
private function getPHPFileClosure(string $filePath): \Closure
{
return \Closure::bind(function () use ($filePath): mixed {
return require $filePath;
}, null, null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Resource\Metadata\Extractor;

use Sylius\Resource\Metadata\ResourceMetadata;

/**
* Extracts an array of metadata from a file or a list of files.
*
* @experimental
*/
interface ResourceExtractorInterface
{
/**
* Parses all metadata files and convert them in an array.
*
* @return ResourceMetadata[]
*/
public function getResources(): array;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Resource\Tests\Metadata\Extractor;

use PHPUnit\Framework\TestCase;
use Sylius\Resource\Metadata\Extractor\PhpFileResourceExtractor;
use Sylius\Resource\Metadata\ResourceMetadata;

final class PhpFileResourceExtractorTest extends TestCase
{
public function testItGetsResourcesFromPhpFileThatReturnsResourceMetadata(): void
{
$extractor = new PhpFileResourceExtractor([__DIR__ . '/php/valid_php_file.php', __DIR__ . '/php/another_valid_php_file.php']);

$expectedResources = [new ResourceMetadata(alias: 'dummy'), new ResourceMetadata(alias: 'another_dummy')];

$this->assertEquals($expectedResources, $extractor->getResources());
}

public function testItExcludesResourcesFromPhpFileThatDoesNotReturnResourceMetadata(): void
{
$extractor = new PhpFileResourceExtractor([__DIR__ . '/php/invalid_php_file.php']);

$this->assertEquals([], $extractor->getResources());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

use Sylius\Resource\Metadata\ResourceMetadata;

return new ResourceMetadata(alias: 'another_dummy');
16 changes: 16 additions & 0 deletions src/Component/tests/Metadata/Extractor/php/invalid_php_file.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

use Sylius\Resource\Tests\Dummy\PullRequest;

return new PullRequest();
16 changes: 16 additions & 0 deletions src/Component/tests/Metadata/Extractor/php/valid_php_file.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

use Sylius\Resource\Metadata\ResourceMetadata;

return new ResourceMetadata(alias: 'dummy');
2 changes: 1 addition & 1 deletion tests/Application/config/packages/framework.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ framework:
secret: "%secret%"
form: ~
csrf_protection: true
default_locale: "%locale%"
enabled_locales: ["%locale%"]
session:
handler_id: ~
storage_factory_id: session.storage.factory.mock_file
Expand Down
Loading