-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
LinksRule for checking validity of links
- Loading branch information
1 parent
6ec9016
commit 5378e63
Showing
25 changed files
with
1,197 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Exceptions; | ||
|
||
use Throwable; | ||
use function sprintf; | ||
|
||
class InvalidLinkDestinationException extends InvalidLinkException | ||
{ | ||
|
||
/** @var string */ | ||
private $destination; | ||
|
||
public function __construct(string $destination, int $code = 0, ?Throwable $previous = null) | ||
{ | ||
parent::__construct(sprintf("Invalid link destination '%s'", $destination), $code, $previous); | ||
$this->destination = $destination; | ||
} | ||
|
||
public function getDestination(): string | ||
{ | ||
return $this->destination; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Exceptions; | ||
|
||
use RuntimeException; | ||
|
||
class InvalidLinkException extends RuntimeException | ||
{ | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Exceptions; | ||
|
||
class InvalidLinkParamsException extends InvalidLinkException | ||
{ | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Exceptions; | ||
|
||
use RuntimeException; | ||
|
||
class LinkCheckFailedException extends RuntimeException | ||
{ | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Nette; | ||
|
||
use Nette\DI\Container; | ||
use PHPStan\ShouldNotHappenException; | ||
use function is_file; | ||
use function is_readable; | ||
use function sprintf; | ||
|
||
class ContainerResolver | ||
{ | ||
|
||
/** @var string|null */ | ||
private $containerLoader; | ||
|
||
/** @var Container|false|null */ | ||
private $container; | ||
|
||
public function __construct(?string $containerLoader) | ||
{ | ||
$this->containerLoader = $containerLoader; | ||
} | ||
|
||
public function getContainer(): ?Container | ||
{ | ||
if ($this->container === false) { | ||
return null; | ||
} | ||
|
||
if ($this->container !== null) { | ||
return $this->container; | ||
} | ||
|
||
if ($this->containerLoader === null) { | ||
$this->container = false; | ||
|
||
return null; | ||
} | ||
|
||
$this->container = $this->loadContainer($this->containerLoader); | ||
|
||
return $this->container; | ||
} | ||
|
||
|
||
private function loadContainer(string $containerLoader): ?Container | ||
{ | ||
if (!is_file($containerLoader)) { | ||
throw new ShouldNotHappenException(sprintf( | ||
'Nette container could not be loaded: file "%s" does not exist', | ||
$containerLoader | ||
)); | ||
} | ||
|
||
if (!is_readable($containerLoader)) { | ||
throw new ShouldNotHappenException(sprintf( | ||
'Nette container could not be loaded: file "%s" is not readable', | ||
$containerLoader | ||
)); | ||
} | ||
|
||
return require $containerLoader; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Nette; | ||
|
||
use Nette\Application\IPresenterFactory; | ||
use Nette\Application\PresenterFactory; | ||
use PHPStan\Exceptions\LinkCheckFailedException; | ||
use PHPStan\ShouldNotHappenException; | ||
use ReflectionClass; | ||
use ReflectionException; | ||
use function count; | ||
use function is_array; | ||
use function is_string; | ||
use function preg_match; | ||
use function preg_replace; | ||
use function sprintf; | ||
use function str_replace; | ||
use function strrpos; | ||
use function substr; | ||
|
||
class PresenterResolver | ||
{ | ||
|
||
/** @var array<string, string|array{0: string, 1: string, 2: string}> */ | ||
protected $mapping; | ||
|
||
/** @var ContainerResolver */ | ||
private $containerResolver; | ||
|
||
/** @var IPresenterFactory */ | ||
private $presenterFactory; | ||
|
||
/** | ||
* @param array<string, string|array{0: string, 1: string, 2: string}> $mapping | ||
*/ | ||
public function __construct(array $mapping, ContainerResolver $containerResolver) | ||
{ | ||
$this->mapping = $mapping; | ||
$this->containerResolver = $containerResolver; | ||
} | ||
|
||
protected function getPresenterFactory(): IPresenterFactory | ||
{ | ||
if ($this->presenterFactory === null) { | ||
if ($this->containerResolver->getContainer() !== null) { | ||
$this->presenterFactory = $this->containerResolver->getContainer()->getByType(IPresenterFactory::class); | ||
} else { | ||
$this->presenterFactory = new PresenterFactory(); | ||
$this->presenterFactory->setMapping($this->mapping); | ||
} | ||
} | ||
return $this->presenterFactory; | ||
} | ||
|
||
/** | ||
* @return array<string, array{0: string, 1: string, 2: string}> | ||
* @throws ShouldNotHappenException | ||
* @throws ReflectionException | ||
*/ | ||
protected function getCurrentMapping(): array | ||
{ | ||
if ($this->mapping !== []) { | ||
$convertedMapping = []; | ||
foreach ($this->mapping as $module => $mask) { | ||
if (is_string($mask)) { | ||
if (preg_match('#^\\\\?([\w\\\\]*\\\\)?(\w*\*\w*?\\\\)?([\w\\\\]*\*\w*)$#D', $mask, $m) !== 1) { | ||
throw new ShouldNotHappenException(sprintf("Invalid mapping mask '%s' in parameters.nette.applicationMapping.", $mask)); | ||
} | ||
$convertedMapping[$module] = [$m[1], $m[2] !== '' ? $m[2] : '*Module\\', $m[3]]; | ||
} elseif (is_array($mask) && count($mask) === 3) { /** @phpstan-ignore-line */ | ||
$convertedMapping[$module] = [$mask[0] !== '' ? $mask[0] . '\\' : '', $mask[1] . '\\', $mask[2]]; | ||
} else { | ||
throw new ShouldNotHappenException(sprintf('Invalid mapping mask for module %s in parameters.nette.applicationMapping.', $module)); | ||
} | ||
} | ||
return $convertedMapping; | ||
} | ||
$presenterFactory = $this->getPresenterFactory(); | ||
if (!$presenterFactory instanceof PresenterFactory) { | ||
throw new ShouldNotHappenException( | ||
'PresenterFactory in your container is not instance of Nette\Application\PresenterFactory. We cannot get mapping from it.' . | ||
' Either set your mappings explicitly in parameters.nette.applicationMapping ' . | ||
' or replace service nettePresenterResolver with your own override of getCurrentMapping() or unformatPresenterClass().' | ||
); | ||
} | ||
$mappingPropertyReflection = (new ReflectionClass($presenterFactory))->getProperty('mapping'); | ||
$mappingPropertyReflection->setAccessible(true); | ||
/** @var array<string, array{0: string, 1: string, 2: string}> $mapping */ | ||
$mapping = $mappingPropertyReflection->getValue($presenterFactory); | ||
return $mapping; | ||
} | ||
public function getPresenterClassByName(string $name, ?string $currentPresenterClass = null): string | ||
{ | ||
$name = $this->resolvePresenterName($name, $currentPresenterClass); | ||
return $this->getPresenterFactory()->getPresenterClass($name); | ||
} | ||
public function resolvePresenterName(string $name, ?string $currentPresenterClass = null): string | ||
{ | ||
if ($name[0] === ':') { | ||
return substr($name, 1); | ||
} | ||
if ($currentPresenterClass === null) { | ||
throw new LinkCheckFailedException(sprintf("Cannot resolve relative presenter name '%s' - current presenter is not set.", $name)); | ||
} | ||
|
||
$currentName = $this->unformatPresenterClass($currentPresenterClass); | ||
$currentNameSepPos = strrpos($currentName, ':'); | ||
if ($currentNameSepPos !== false && $currentNameSepPos !== 0) { | ||
$currentModule = substr($currentName, 0, $currentNameSepPos); | ||
$currentPresenter = substr($currentName, $currentNameSepPos + 1); | ||
} else { | ||
$currentModule = ''; | ||
$currentPresenter = $currentName; | ||
} | ||
|
||
if ($name === 'this') { | ||
return $currentModule . ':' . $currentPresenter; | ||
} | ||
|
||
return $currentModule . ':' . $name; | ||
} | ||
|
||
protected function unformatPresenterClass(string $class): string | ||
{ | ||
foreach ($this->getCurrentMapping() as $module => $mapping) { | ||
$mapping = str_replace(['\\', '*'], ['\\\\', '(\w+)'], $mapping); | ||
if (preg_match('#^\\\\?' . $mapping[0] . '((?:' . $mapping[1] . ')*)' . $mapping[2] . '$#Di', $class, $matches) === 1) { | ||
return ($module === '*' ? '' : $module . ':') | ||
. preg_replace('#' . $mapping[1] . '#iA', '$1:', $matches[1]) . $matches[3]; | ||
} | ||
} | ||
|
||
throw new LinkCheckFailedException(sprintf("Cannot convert presenter class '%s' to presenter name. No matching mapping found.", $class)); | ||
} | ||
|
||
} |
Oops, something went wrong.