Skip to content

Commit

Permalink
[BCB] Resolving relative paths in NEON according to config file place…
Browse files Browse the repository at this point in the history
…ment
  • Loading branch information
ondrejmirtes committed Sep 13, 2019
1 parent 2a21418 commit 0c17a5b
Show file tree
Hide file tree
Showing 17 changed files with 323 additions and 11 deletions.
6 changes: 3 additions & 3 deletions README.md
Expand Up @@ -191,9 +191,9 @@ you can specify directories to scan and concrete files to include using
```
parameters:
autoload_directories:
- %rootDir%/../../../build
- build
autoload_files:
- %rootDir%/../../../generated/routes/GeneratedRouteList.php
- generated/routes/GeneratedRouteList.php
```

`%rootDir%` is expanded to the root directory where PHPStan resides.
Expand Down Expand Up @@ -364,7 +364,7 @@ you can provide your own bootstrap file:

```
parameters:
bootstrap: %rootDir%/../../../phpstan-bootstrap.php
bootstrap: phpstan-bootstrap.php
```

### Custom rules
Expand Down
3 changes: 3 additions & 0 deletions build/phpstan.neon
Expand Up @@ -50,6 +50,9 @@ parameters:
-
message: '#^Constant SOME_CONSTANT_IN_AUTOLOAD_FILE not found\.$#'
path: %rootDir%/tests/PHPStan/Command/AnalyseCommandTest.php
-
message: '#^Fetching class constant PREVENT_MERGING of deprecated class Nette\\DI\\Config\\Helpers\.$#'
path: ../src/DependencyInjection/NeonAdapter.php
reportStaticMethodSignatures: true
reportUnmatchedIgnoredErrors: false
tmpDir: %rootDir%/tmp
Expand Down
1 change: 1 addition & 0 deletions composer.json
Expand Up @@ -10,6 +10,7 @@
"jean85/pretty-package-versions": "^1.0.3",
"nette/bootstrap": "^3.0",
"nette/di": "^3.0",
"nette/neon": "^3.0",
"nette/robot-loader": "^3.0.1",
"nette/schema": "^1.0",
"nette/utils": "^3.0",
Expand Down
18 changes: 12 additions & 6 deletions src/Command/CommandHelper.php
Expand Up @@ -2,7 +2,6 @@

namespace PHPStan\Command;

use Nette\DI\Config\Adapters\NeonAdapter;
use Nette\DI\Config\Adapters\PhpAdapter;
use Nette\DI\Helpers;
use Nette\Schema\Context as SchemaContext;
Expand All @@ -11,6 +10,7 @@
use Nette\Utils\Validators;
use PHPStan\DependencyInjection\ContainerFactory;
use PHPStan\DependencyInjection\LoaderFactory;
use PHPStan\DependencyInjection\NeonAdapter;
use PHPStan\File\FileFinder;
use PHPStan\File\FileHelper;
use Symfony\Component\Console\Input\InputInterface;
Expand Down Expand Up @@ -53,7 +53,6 @@ public static function begin(
}
$fileHelper = new FileHelper($currentWorkingDirectory);
if ($autoloadFile !== null) {
$autoloadFile = $fileHelper->absolutizePath($autoloadFile);
if (!is_file($autoloadFile)) {
$errorOutput->writeln(sprintf('Autoload file "%s" not found.', $autoloadFile));
throw new \PHPStan\Command\InceptionNotSuccessfulException();
Expand Down Expand Up @@ -243,9 +242,13 @@ public static function begin(
}

foreach ($container->getParameter('autoload_files') as $parameterAutoloadFile) {
if (!file_exists($parameterAutoloadFile)) {
$errorOutput->writeln(sprintf('Autoload file %s does not exist.', $parameterAutoloadFile));
throw new \PHPStan\Command\InceptionNotSuccessfulException();
}
(static function (string $file) use ($container): void {
require_once $file;
})($fileHelper->normalizePath($parameterAutoloadFile));
})($parameterAutoloadFile);
}

$autoloadDirectories = $container->getParameter('autoload_directories');
Expand All @@ -257,19 +260,22 @@ public static function begin(

$robotLoader->setTempDirectory($tmpDir);
foreach ($autoloadDirectories as $directory) {
$robotLoader->addDirectory($fileHelper->normalizePath($directory));
if (!file_exists($directory)) {
$errorOutput->writeln(sprintf('Autoload directory %s does not exist.', $directory));
throw new \PHPStan\Command\InceptionNotSuccessfulException();
}
$robotLoader->addDirectory($directory);
}

foreach ($container->getParameter('excludes_analyse') as $directory) {
$robotLoader->excludeDirectory($fileHelper->normalizePath($directory));
$robotLoader->excludeDirectory($directory);
}

$robotLoader->register();
}

$bootstrapFile = $container->getParameter('bootstrap');
if ($bootstrapFile !== null) {
$bootstrapFile = $fileHelper->normalizePath($bootstrapFile);
if (!is_file($bootstrapFile)) {
$errorOutput->writeln(sprintf('Bootstrap file %s does not exist.', $bootstrapFile));
throw new \PHPStan\Command\InceptionNotSuccessfulException();
Expand Down
14 changes: 14 additions & 0 deletions src/DependencyInjection/Configurator.php
Expand Up @@ -3,6 +3,7 @@
namespace PHPStan\DependencyInjection;

use Nette\DI\Config\Loader;
use Nette\DI\ContainerLoader;

class Configurator extends \Nette\Configurator
{
Expand Down Expand Up @@ -30,4 +31,17 @@ protected function getDefaultParameters(): array
return [];
}

public function loadContainer(): string
{
$loader = new ContainerLoader(
$this->getCacheDirectory() . '/nette.configurator',
$this->parameters['debugMode']
);

return $loader->load(
[$this, 'generateContainer'],
[$this->parameters, array_keys($this->dynamicParameters), $this->configs, PHP_VERSION_ID - PHP_RELEASE_VERSION, NeonAdapter::CACHE_KEY]
);
}

}
2 changes: 1 addition & 1 deletion src/DependencyInjection/LoaderFactory.php
Expand Up @@ -2,7 +2,6 @@

namespace PHPStan\DependencyInjection;

use Nette\DI\Config\Adapters\NeonAdapter;
use Nette\DI\Config\Loader;

class LoaderFactory
Expand All @@ -27,6 +26,7 @@ public function createLoader(): Loader
{
$loader = new Loader();
$loader->addAdapter('dist', NeonAdapter::class);
$loader->addAdapter('neon', NeonAdapter::class);
$loader->setParameters([
'rootDir' => $this->rootDir,
'currentWorkingDirectory' => $this->currentWorkingDirectory,
Expand Down
170 changes: 170 additions & 0 deletions src/DependencyInjection/NeonAdapter.php
@@ -0,0 +1,170 @@
<?php declare(strict_types = 1);

namespace PHPStan\DependencyInjection;

use Nette\DI\Config\Adapter;
use Nette\DI\Config\Helpers;
use Nette\DI\Definitions\Reference;
use Nette\DI\Definitions\Statement;
use Nette\Neon\Entity;
use Nette\Neon\Neon;
use PHPStan\File\FileHelper;

class NeonAdapter implements Adapter
{

public const CACHE_KEY = 'v1';

private const PREVENT_MERGING_SUFFIX = '!';

/** @var FileHelper[] */
private $fileHelpers;

/**
* @param string $file
* @return mixed[]
*/
public function load(string $file): array
{
$contents = file_get_contents($file);
if ($contents === false) {
throw new \PHPStan\ShouldNotHappenException();
}
return $this->process((array) Neon::decode($contents), '', $file);
}

/**
* @param mixed[] $arr
* @return mixed[]
*/
public function process(array $arr, string $fileKey, string $file): array
{
$res = [];
foreach ($arr as $key => $val) {
if (is_string($key) && substr($key, -1) === self::PREVENT_MERGING_SUFFIX) {
if (!is_array($val) && $val !== null) {
throw new \Nette\DI\InvalidConfigurationException(sprintf('Replacing operator is available only for arrays, item \'%s\' is not array.', $key));
}
$key = substr($key, 0, -1);
$val[Helpers::PREVENT_MERGING] = true;
}

if (is_array($val)) {
if (!is_int($key)) {
$fileKeyToPass = $fileKey . '[' . $key . ']';
} else {
$fileKeyToPass = $fileKey . '[]';
}
$val = $this->process($val, $fileKeyToPass, $file);

} elseif ($val instanceof Entity) {
if (!is_int($key)) {
$fileKeyToPass = $fileKey . '(' . $key . ')';
} else {
$fileKeyToPass = $fileKey . '()';
}
if ($val->value === Neon::CHAIN) {
$tmp = null;
foreach ($this->process($val->attributes, $fileKeyToPass, $file) as $st) {
$tmp = new Statement(
$tmp === null ? $st->getEntity() : [$tmp, ltrim(implode('::', (array) $st->getEntity()), ':')],
$st->arguments
);
}
$val = $tmp;
} else {
$tmp = $this->process([$val->value], $fileKeyToPass, $file);
$val = new Statement($tmp[0], $this->process($val->attributes, $fileKeyToPass, $file));
}
}
if ((in_array($fileKey, [
'[parameters][autoload_files]',
'[parameters][autoload_directories]',
'[parameters][paths]',
'[parameters][excludes_analyse]',
'[parameters][ignoreErrors][][paths]',
], true) || (
$fileKey === '[parameters]'
&& in_array($key, [
'bootstrap',
'memoryLimitFile',
'benchmarkFile',
], true)
) || (
$fileKey === '[parameters][ignoreErrors][]'
&& in_array($key, [
'path',
], true)
)) && is_string($val) && strpos($val, '%') === false) {
$fileHelper = $this->createFileHelperByFile($file);
$val = $fileHelper->normalizePath($fileHelper->absolutizePath($val));
}
$res[$key] = $val;
}
return $res;
}

/**
* @param mixed[] $data
* @return string
*/
public function dump(array $data): string
{
array_walk_recursive(
$data,
static function (&$val): void {
if (!($val instanceof Statement)) {
return;
}

$val = self::statementToEntity($val);
}
);
return "# generated by Nette\n\n" . Neon::encode($data, Neon::BLOCK);
}

private static function statementToEntity(Statement $val): Entity
{
array_walk_recursive(
$val->arguments,
static function (&$val): void {
if ($val instanceof Statement) {
$val = self::statementToEntity($val);
} elseif ($val instanceof Reference) {
$val = '@' . $val->getValue();
}
}
);

$entity = $val->getEntity();
if ($entity instanceof Reference) {
$entity = '@' . $entity->getValue();
} elseif (is_array($entity)) {
if ($entity[0] instanceof Statement) {
return new Entity(
Neon::CHAIN,
[
self::statementToEntity($entity[0]),
new Entity('::' . $entity[1], $val->arguments),
]
);
} elseif ($entity[0] instanceof Reference) {
$entity = '@' . $entity[0]->getValue() . '::' . $entity[1];
} elseif (is_string($entity[0])) {
$entity = $entity[0] . '::' . $entity[1];
}
}
return new Entity($entity, $val->arguments);
}

private function createFileHelperByFile(string $file): FileHelper
{
$dir = dirname($file);
if (!isset($this->fileHelpers[$dir])) {
$this->fileHelpers[$dir] = new FileHelper($dir);
}

return $this->fileHelpers[$dir];
}

}

0 comments on commit 0c17a5b

Please sign in to comment.