Skip to content

Commit

Permalink
Add PHPStan gubbins.
Browse files Browse the repository at this point in the history
  - Add PHPStan and "phpstan-strict-rules" gubbins.
  - Address Level 9 issues raised by PHPStan in the static site
    generator code base.
  - "Taskfile.yml": Add static analysis to "ci" task.
  • Loading branch information
damiendart committed Aug 23, 2024
1 parent 58148a2 commit b4ea963
Show file tree
Hide file tree
Showing 15 changed files with 192 additions and 27 deletions.
16 changes: 16 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ tasks:
- task: 'dependencies'
- task: 'build'
- task: 'lint'
- task: 'static-analysis'
desc: 'Run all default CI-related tasks'

ci:end-to-end:
Expand Down Expand Up @@ -190,6 +191,21 @@ tasks:
- task: 'ci'
desc: 'Run all development environment setup tasks'

static-analysis:
cmds:
- task: 'static-analysis:php'
desc: 'Run all static-analysis-related tasks'

static-analysis:php:
cmds:
- task: 'static-analysis:php:phpstan'
desc: 'Run all PHP static-analysis-related tasks'

static-analysis:php:phpstan:
cmds:
- 'protected/vendor/bin/phpstan analyse --no-progress'
desc: 'Analyse PHP files with PHPStan'

test:end-to-end:
cmds:
- 'protected/vendor/bin/phpunit --testdox'
Expand Down
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
"friendsofphp/php-cs-fixer": "^3.59.3",
"league/commonmark": "^2.4",
"michelf/php-smartypants": "^1.8.1",
"phpstan/phpstan": "^1.11",
"phpstan/phpstan-strict-rules": "^1.6",
"phpunit/phpunit": "^11.2.5",
"roave/security-advisories": "dev-latest",
"symfony/yaml": "^7.1",
Expand Down
109 changes: 108 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (C) Damien Dart, <[email protected]>.
# This file is distributed under the MIT licence. For more information,
# please refer to the accompanying "LICENCE" file.

includes:
- 'protected/vendor/phpstan/phpstan-strict-rules/rules.neon'
parameters:
level: 9
paths:
- 'src/ssg'
12 changes: 10 additions & 2 deletions src/ssg/InputFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
{
private ?string $modifiedContent;

/** @param mixed[] $metadata */
public function __construct(
public string $source,
public string $outputPath,
Expand All @@ -35,10 +36,16 @@ public function getContent(): string

private function getOriginalContent(): string
{
// TODO: Handle false return.
return file_get_contents($this->source);
$content = file_get_contents($this->source);

// Errors are converted into exceptions by the custom error
// handler, so "file_get_contents" will always return a string.
\assert(\is_string($content));

return $content;
}

/** @param mixed[] $metadata */
public function withAdditionalMetadata(array $metadata): self
{
return $this->withMetadata(
Expand Down Expand Up @@ -66,6 +73,7 @@ public function withOutputPath(string $outputPath): self
);
}

/** @param mixed[] $metadata */
public function withMetadata(array $metadata): self
{
return new self(
Expand Down
1 change: 1 addition & 0 deletions src/ssg/Pipeline.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

final readonly class Pipeline implements StepInterface
{
/** @var StepInterface[] */
private array $steps;

public function __construct(StepInterface ...$steps)
Expand Down
3 changes: 3 additions & 0 deletions src/ssg/StaticSiteGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public function run(): int
return 0;
}

/** @return InputFile[] */
private function getInputFiles(): array
{
$files = new \RecursiveCallbackFilterIterator(
Expand All @@ -70,6 +71,8 @@ private function getInputFiles(): array
$inputDirectory = realpath($this->inputDirectory);
$inputFiles = [];

\assert(\is_string($inputDirectory));

/** @var \SplFileInfo $file */
foreach (new \RecursiveIteratorIterator($files) as $file) {
$inputFiles[] = new InputFile(
Expand Down
4 changes: 2 additions & 2 deletions src/ssg/Steps/GenerateSlugsStep.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace StaticSiteGenerator\Steps;

use StaticSiteGenerator\Inputfile;
use StaticSiteGenerator\InputFile;

final class GenerateSlugsStep extends AbstractStep
{
Expand All @@ -23,7 +23,7 @@ final class GenerateSlugsStep extends AbstractStep
/** @var string[] */
private const TEMPLATE_EXTENSIONS = ['twig'];

protected function process(Inputfile $inputFile): Inputfile
protected function process(InputFile $inputFile): InputFile
{
return $inputFile->withAdditionalMetadata(
['slug' => $this->slugify($inputFile->outputPath)],
Expand Down
17 changes: 11 additions & 6 deletions src/ssg/Steps/MinifyHtmlStep.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace StaticSiteGenerator\Steps;

use StaticSiteGenerator\Inputfile;
use StaticSiteGenerator\InputFile;
use voku\helper\HtmlMin;

final class MinifyHtmlStep extends AbstractStep
Expand All @@ -27,7 +27,7 @@ public function __construct()
->doMakeSameDomainsLinksRelative(['www.robotinaponcho.net']);
}

protected function process(Inputfile $inputFile): Inputfile
protected function process(InputFile $inputFile): InputFile
{
if (! str_ends_with($inputFile->outputPath, 'html')) {
return $inputFile;
Expand Down Expand Up @@ -85,12 +85,14 @@ private function processContent(string $content): string
$content = $this->minifier->minify($content);

// HTMLMin adds superfluous whitespace between block elements.
$content = preg_replace(
// Errors are converted into exceptions by the custom error
// handler, so "preg_replace" will always return a string.
$content = (string) preg_replace(
"/({$elementRegexGroup})>\\s</",
'$1><',
$content,
);
$content = preg_replace(
$content = (string) preg_replace(
"#>\\s<(/?({$elementRegexGroup}))#",
'><$1',
$content,
Expand All @@ -100,14 +102,17 @@ private function processContent(string $content): string
// Add the scheme and domain name back to canonical link
// URLs to prevent any future shenanigans (if the site gets
// mirrored, for example).
$content = preg_replace(
$content = (string) preg_replace(
'/<link href=(\S+) rel=canonical>/',
'<link href=https://www.robotinaponcho.net$1 rel=canonical>',
$content,
);
}

return preg_replace_callback(
// Errors are converted into exceptions by the custom error
// handler, so "preg_replace_callback" will always return a
// string in this instance.
return (string) preg_replace_callback(
'/(&#\d+;)/',
static function ($match): string {
return mb_convert_encoding(
Expand Down
10 changes: 5 additions & 5 deletions src/ssg/Steps/ProcessFrontMatterStep.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace StaticSiteGenerator\Steps;

use StaticSiteGenerator\Inputfile;
use StaticSiteGenerator\InputFile;
use StaticSiteGenerator\ValueObjects\GitMetadata;
use Symfony\Component\Yaml\Parser;

Expand All @@ -25,7 +25,7 @@ public function __construct(
private readonly Parser $yamlParser,
) {}

protected function process(Inputfile $inputFile): Inputfile
protected function process(InputFile $inputFile): InputFile
{
foreach (self::FRONT_MATTER_REGEXES as $regex) {
if (
Expand All @@ -34,7 +34,7 @@ protected function process(Inputfile $inputFile): Inputfile
) {
$content = str_replace($matches[0], '', $inputFile->getContent());

/** @var array{array-key, mixed} $metadata */
/** @var mixed[] $metadata */
$metadata = $this->yamlParser->parse($matches[1]) ?? [];

if (\array_key_exists('git', $metadata)) {
Expand All @@ -59,8 +59,8 @@ private function parseGitMetadataKeyword(string $keyword): GitMetadata
}

return new GitMetadata(
\DateTimeImmutable::createFromFormat('U', $parts[2]),
\DateTimeImmutable::createFromFormat('U', $parts[4]),
new \DateTimeImmutable('@' . $parts[2]),
new \DateTimeImmutable('@' . $parts[4]),
$parts[1],
$parts[3],
);
Expand Down
5 changes: 4 additions & 1 deletion src/ssg/Steps/ProcessMarkdownStep.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ private function extractTitle(InputFile $inputFile): InputFile
) {
return $inputFile
->withContent(
preg_replace(
// Errors are converted into exceptions by the
// custom error handler, so "preg_replace" will
// always return a string.
(string) preg_replace(
"/{$headings[1]}\n=+\n/",
'',
$content,
Expand Down
4 changes: 2 additions & 2 deletions src/ssg/Steps/StepInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@

namespace StaticSiteGenerator\Steps;

use StaticSiteGenerator\Inputfile;
use StaticSiteGenerator\InputFile;

interface StepInterface
{
/** @return InputFile[] */
public function run(Inputfile ...$inputFiles): array;
public function run(InputFile ...$inputFiles): array;
}
5 changes: 4 additions & 1 deletion src/ssg/Support/TwigEnvironmentFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ static function (?string $string): string {
return $string;
}

return preg_replace(
// Errors are converted into exceptions by the
// custom error handler, so "preg_replace" will
// always return a string.
return (string) preg_replace(
'/\s+(\S+)$/',
'&#160;$1',
rtrim($string),
Expand Down
Loading

0 comments on commit b4ea963

Please sign in to comment.