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
14 changes: 14 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
*.php text eol=lf
*.phpt text eol=lf
/.gitattributes export-ignore
/.github/ export-ignore
/.gitignore export-ignore
/composer.lock export-ignore
/docs/ export-ignore
/phpcs.xml.dist export-ignore
/phpstan.neon export-ignore
/phpunit.xml.dist export-ignore
/psalm.xml export-ignore
/psalm-baseline.xml export-ignore
/tests/ export-ignore
/tools/ export-ignore
48 changes: 48 additions & 0 deletions .github/workflows/docs-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# https://help.github.com/en/categories/automating-your-workflow-with-github-actions

name: "Check Docs"

on:
pull_request:
push:
branches:
- "[0-9]+.[0-9]+.x"

jobs:
checkdocs:
name: "Check Docs"

runs-on: ${{ matrix.operating-system }}

strategy:
matrix:
dependencies:
- "locked"
php-version:
- "8.4"
operating-system:
- "ubuntu-latest"

steps:
- name: "Checkout"
uses: actions/checkout@v6

- name: "Install PHP"
uses: "shivammathur/setup-php@2.37.2"
with:
coverage: none
php-version: "${{ matrix.php-version }}"
ini-values: memory_limit=-1, opcache.enable_cli=1

- uses: ramsey/composer-install@4.0.0
with:
dependency-versions: ${{ matrix.dependencies }}

- name: "extract php code"
run: "bin/docs-extract-php-code"

- name: "lint php"
run: "php -l docs_php/*.php"

- name: "docs code style"
run: "vendor/bin/phpcbf docs_php --exclude=SlevomatCodingStandard.TypeHints.DeclareStrictTypes,SlevomatCodingStandard.ControlStructures.EarlyExit"
22 changes: 22 additions & 0 deletions .github/workflows/docs-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Publish docs

on:
push:
branches:
- "[0-9]+.[0-9]+.x"
release:
types:
- published

jobs:
trigger:
runs-on: ubuntu-latest
steps:
- name: Trigger workflow in other repo
run: |
curl -L -X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ secrets.ORGANIZATION_ADMIN_TOKEN }}" \
-H "X-GitHub-Api-Version: 2026-03-10" \
https://api.github.com/repos/patchlevel/patchlevel.dev/actions/workflows/prod-deployment.yaml/dispatches \
-d '{"ref":"main"}'
22 changes: 22 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,25 @@ phpstan-baseline: vendor

.PHONY: static
static: phpstan cs ## run static analyser

.PHONY: docs
docs: docs-extract-php docs-php-lint docs-phpcs docs-inject-php

.PHONY: docs-extract-php
docs-extract-php:
bin/docs-extract-php-code

.PHONY: docs-inject-php
docs-inject-php:
bin/docs-inject-php-code

.PHONY: docs-format ## format docs
docs-format: docs-phpcs docs-inject-php

.PHONY: docs-php-lint ## lint docs code
docs-php-lint: docs-extract-php
php -l docs_php/*.php | grep 'Parse error: ' || true

.PHONY: docs-phpcs
docs-phpcs: docs-extract-php
vendor/bin/phpcbf docs_php --exclude=SlevomatCodingStandard.TypeHints.DeclareStrictTypes,SlevomatCodingStandard.ControlStructures.EarlyExit || true
36 changes: 29 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,40 @@
[![License](https://poser.pugx.org/patchlevel/event-sourcing-psalm-plugin/license)](//packagist.org/packages/patchlevel/event-sourcing-psalm-plugin)
[![Latest Stable Version](https://poser.pugx.org/patchlevel/event-sourcing-phpstan-extension/v)](//packagist.org/packages/patchlevel/event-sourcing-phpstan-extension)
[![License](https://poser.pugx.org/patchlevel/event-sourcing-phpstan-extension/license)](//packagist.org/packages/patchlevel/event-sourcing-phpstan-extension)

# event-sourcing-phpstan-extension

phpstan extension for [event-sourcing](https://github.com/patchlevel/event-sourcing) library.
"PHPStan that understands your aggregates and catches event sourcing mistakes before runtime."

## installation
## Features

```
* [Property initialization](https://patchlevel.dev/docs/event-sourcing-phpstan-extension/latest/getting-started#property-initialization) for aggregate roots and child aggregates, so PHPStan does not report false uninitialized property errors.
* [Recording in apply methods](https://patchlevel.dev/docs/event-sourcing-phpstan-extension/latest/getting-started#recording-in-apply-methods) is reported as an error, because recording events while replaying them leads to duplicated events.

## Installation

```bash
composer require --dev patchlevel/event-sourcing-phpstan-extension
```

add the extension to your `phpstan.neon`:
Register the extension in your `phpstan.neon`:

```neon
includes:
- vendor/patchlevel/event-sourcing-phpstan-extension/extension.neon
```
- vendor/patchlevel/event-sourcing-phpstan-extension/extension.neon
```

## Documentation

* Latest [Docs](https://patchlevel.dev/docs/event-sourcing-phpstan-extension/latest)
* Related [Blog](https://patchlevel.dev/blog)

## Integration

* [patchlevel/event-sourcing](https://github.com/patchlevel/event-sourcing)

## Contributing

We are open to contributions as long as they are in line with
our [BC-Policy](https://patchlevel.dev/our-backward-compatibility-promise).

Also note that the `composer.lock` is always generated with the newest supported PHP version as this is the version our tools run in the CI.
53 changes: 53 additions & 0 deletions bin/docs-extract-php-code
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env php
<?php

use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode;
use League\CommonMark\Node\Query;
use League\CommonMark\Parser\MarkdownParser;
use Wnx\CommonmarkMarkdownRenderer\MarkdownRendererExtension;

require __DIR__ . '/../vendor/autoload.php';

$environment = new Environment([]);
$environment->addExtension(new MarkdownRendererExtension());

$parser = new MarkdownParser($environment);

$targetDir = __DIR__ . '/../docs_php';

if (file_exists($targetDir)) {
exec('rm -rf ' . $targetDir);
}

mkdir($targetDir);

$finder = new Symfony\Component\Finder\Finder();
$finder->files()->in(__DIR__ . '/../docs')->name('*.md');

foreach ($finder as $file) {
$fileName = pathinfo($file->getBasename(), PATHINFO_FILENAME);

$content = file_get_contents($file->getPathname());
$document = $parser->parse($content);

$result = (new Query())
->where(Query::type(FencedCode::class))
->findAll($document);

/**
* @var FencedCode $node
*/
foreach ($result as $i => $node) {
if ($node->getInfo() !== 'php') {
continue;
}

$source = sprintf('%s:%s', $file->getRealPath(), $node->getStartLine());

$code = "<?php\n// " . $source . "\n\n" . $node->getLiteral();

$targetPath = $targetDir . '/' . $fileName . '_' . $i . '.php';
file_put_contents($targetPath, $code);
}
}
70 changes: 70 additions & 0 deletions bin/docs-inject-php-code
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env php
<?php

use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode;
use League\CommonMark\Node\Query;
use League\CommonMark\Parser\MarkdownParser;
use Wnx\CommonmarkMarkdownRenderer\MarkdownRendererExtension;
use Wnx\CommonmarkMarkdownRenderer\Renderer\MarkdownRenderer;

require __DIR__ . '/../vendor/autoload.php';



$environment = new Environment([]);
$environment->addExtension(new MarkdownRendererExtension());

$parser = new MarkdownParser($environment);
$markdownRenderer = new MarkdownRenderer($environment);

$targetDir = __DIR__ . '/../docs_php';

if (!file_exists($targetDir)) {
exit(1);
}

$finder = new Symfony\Component\Finder\Finder();
$finder->files()->in(__DIR__ . '/../docs')->name('*.md');

foreach ($finder as $file) {
$fileName = pathinfo($file->getBasename(), PATHINFO_FILENAME);

$content = file_get_contents($file->getPathname());
$document = $parser->parse($content);

$result = (new Query())
->where(Query::type(FencedCode::class))
->findAll($document);

/**
* @var FencedCode $node
*/
foreach ($result as $i => $node) {
if ($node->getInfo() !== 'php') {
$node->setLiteral(trim($node->getLiteral()));
continue;
}

$targetPath = $targetDir . '/' . $fileName . '_' . $i . '.php';

if (!file_exists($targetPath)) {
$node->setLiteral(trim($node->getLiteral()));
continue;
}

$code = file_get_contents($targetPath);

$lines = explode("\n", $code);
array_splice($lines, 0, 2);
$code = implode("\n", $lines);

$node->setLiteral(trim($code));
}

file_put_contents($file->getPathname(), $markdownRenderer->renderDocument($document));
}

if (file_exists($targetDir)) {
exec('rm -rf ' . $targetDir);
}
12 changes: 8 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
"name": "patchlevel/event-sourcing-phpstan-extension",
"type": "phpstan-extension",
"license": "MIT",
"description": "phpstan extension for patchlevel/event-sourcing",
"description": "PHPStan that understands your aggregates and catches event sourcing mistakes before runtime",
"keywords": [
"patchlevel",
"event-sourcing",
"phpstan"
"phpstan",
"static-analysis",
"aggregate"
],
"homepage": "https://github.com/patchlevel/event-sourcing-phpstan-extension",
"homepage": "https://patchlevel.dev/docs/event-sourcing-phpstan-extension/latest",
"authors": [
{
"name": "Daniel Badura",
Expand All @@ -26,7 +29,8 @@
"patchlevel/coding-standard": "^1.3.0",
"patchlevel/event-sourcing": "^3.4.0",
"phpstan/phpstan-strict-rules": "^2.0.4",
"roave/security-advisories": "dev-master"
"roave/security-advisories": "dev-master",
"wnx/commonmark-markdown-renderer": "^1.6"
},
"config": {
"preferred-install": {
Expand Down
Loading