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
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ AI-агенты склонны отклоняться от конвенций.
| `phpcs.xml.dist` | PHP CodeSniffer |
| `phpunit.xml.dist` | PHPUnit |
| `phpmd.xml` | PHPMD |
| `phpstan.neon.dist` | PHPStan |
| `psalm.xml` | Psalm |
| `Makefile` | Команды проверки (`make check`) |

Expand All @@ -71,7 +72,9 @@ composer require --dev prikotov/coding-standard

- **Сниффы** — PHP CodeSniffer-правила, работают сразу из `vendor/`
- **Deptrac-правила** — пользовательские правила для deptrac
- **Конфигурации** — `depfile.yaml` для Deptrac, `phpcs.xml.dist` для PHPCS
- **PHPStan-правила** — пользовательские правила для phpstan
- **Конфигурации** — `depfile.yaml` для Deptrac, `phpcs.xml.dist` для PHPCS, `phpstan.neon.dist` для PHPStan
- **Шаблоны исключений** — типовые классы и интерфейсы, копируются с подстановкой namespace проекта
- **Конвенции** — документация, копируется командой `coding-standard-init`

### Подключение PHPCS
Expand All @@ -93,6 +96,24 @@ php vendor/bin/coding-standard-init
php vendor/bin/coding-standard-init /path/to/project --docs-path=docs/ddd --deptrac-path=config/depfile.yaml --force
```

### Копирование типовых исключений

Шаблоны исключений хранятся в `config/exceptions/` и копируются в проект с подстановкой имени namespace.

```bash
php vendor/bin/coding-standard-init --project-name=Task
```

Это создаст файлы в `src/Common/Exception/` с namespace `Task\Common\Exception`.

| Опция | Описание |
|---|---|
| `--project-name=Task` | Имя проекта для namespace (обязательно для исключений) |
| `--exceptions-path=src/Common/Exception` | Путь копирования (по умолчанию) |
| `--no-exceptions` | Пропустить копирование исключений |

Без `--project-name` исключения пропускаются, остальные файлы копируются как обычно.

---

## License
Expand Down
94 changes: 82 additions & 12 deletions bin/coding-standard-init
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,28 @@ declare(strict_types=1);
*
* Usage:
* php vendor/bin/coding-standard-init [target-dir] [--docs-path=<path>] [--deptrac-path=<path>] [--force]
* [--project-name=<Name>] [--exceptions-path=<path>] [--no-exceptions]
*
* target-dir — project root (default: current working directory).
* --docs-path — relative path where docs will be copied (default: docs/conventions).
* --deptrac-path — relative path where depfile.yaml will be copied (default: depfile.yaml).
* --no-deptrac — skip depfile.yaml copying entirely.
* --force — overwrite existing files with fresh copies from the package.
* target-dir — project root (default: current working directory).
* --docs-path — relative path where docs will be copied (default: docs/conventions).
* --deptrac-path — relative path where depfile.yaml will be copied (default: depfile.yaml).
* --no-deptrac — skip depfile.yaml copying entirely.
* --project-name — project name used as namespace prefix for exceptions (e.g. Task).
* --exceptions-path — relative path where exceptions will be copied (default: src/Common/Exception).
* --no-exceptions — skip exceptions copying entirely.
* --force — overwrite existing files with fresh copies from the package.
*/

// ── Parse arguments ────────────────────────────────────────────────────────

$force = false;
$noDeptrac = false;
$targetArg = null;
$docsPath = null;
$deptracPath = null;
$force = false;
$noDeptrac = false;
$noExceptions = false;
$targetArg = null;
$docsPath = null;
$deptracPath = null;
$projectName = null;
$exceptionsPath = null;

foreach ($argv as $i => $arg) {
if ($i === 0) {
Expand All @@ -32,10 +39,16 @@ foreach ($argv as $i => $arg) {
$force = true;
} elseif ($arg === '--no-deptrac') {
$noDeptrac = true;
} elseif ($arg === '--no-exceptions') {
$noExceptions = true;
} elseif (str_starts_with($arg, '--docs-path=')) {
$docsPath = substr($arg, strlen('--docs-path='));
} elseif (str_starts_with($arg, '--deptrac-path=')) {
$deptracPath = substr($arg, strlen('--deptrac-path='));
} elseif (str_starts_with($arg, '--project-name=')) {
$projectName = substr($arg, strlen('--project-name='));
} elseif (str_starts_with($arg, '--exceptions-path=')) {
$exceptionsPath = substr($arg, strlen('--exceptions-path='));
} else {
$targetArg = $arg;
}
Expand All @@ -59,7 +72,8 @@ if ($targetDir === false || !is_dir($targetDir)) {

$sourceDocs = $packageRoot . '/docs/conventions';
$sourceConfig = $packageRoot . '/config';
$docsPath = $docsPath ?? 'docs/conventions';
$docsPath = $docsPath ?? 'docs/conventions';
$exceptionsPath = $exceptionsPath ?? 'src/Common/Exception';
$targetDocs = $targetDir . '/' . $docsPath;

if (!is_dir($sourceDocs)) {
Expand Down Expand Up @@ -168,7 +182,60 @@ if (file_exists($phpstanSource)) {
}
}

// ── 5. Update .gitignore ──────────────────────────────────────────────────
// ── 5. Copy exceptions ────────────────────────────────────────────────────

if (!$noExceptions && $projectName !== null) {
$exceptionsSource = $sourceConfig . '/exceptions';

if (is_dir($exceptionsSource)) {
$targetExceptions = $targetDir . '/' . $exceptionsPath;
echo " Using project name: $projectName" . PHP_EOL;

$exIterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($exceptionsSource, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST,
);

foreach ($exIterator as $item) {
$relativePath = substr($item->getPathname(), strlen($exceptionsSource) + 1);
$destPath = $targetExceptions . '/' . $relativePath;

if ($item->isDir()) {
if (!is_dir($destPath)) {
mkdir($destPath, 0755, true);
}
continue;
}

$content = file_get_contents($item->getPathname());
$content = str_replace('{{ProjectName}}', $projectName, $content);

$destDirForFile = dirname($destPath);
if (!is_dir($destDirForFile)) {
mkdir($destDirForFile, 0755, true);
}

if (file_exists($destPath)) {
if ($force) {
file_put_contents($destPath, $content);
echo " Updated $exceptionsPath/$relativePath" . PHP_EOL;
$updated++;
} else {
echo " Skip $exceptionsPath/$relativePath (already exists)" . PHP_EOL;
$skipped++;
}
} else {
file_put_contents($destPath, $content);
echo " Copied $exceptionsPath/$relativePath" . PHP_EOL;
$copied++;
}
}
}
} elseif (!$noExceptions && $projectName === null) {
echo " Skip exceptions (--project-name not specified)" . PHP_EOL;
}

// ── 6. Update .gitignore ──────────────────────────────────────────────────

// 4a. docs/.gitignore — exclude copied conventions documentation

Expand Down Expand Up @@ -204,3 +271,6 @@ if ($deptracPath !== '') {
echo " 3. Run \`vendor/bin/deptrac analyse\` to verify architectural rules." . PHP_EOL;
}
echo " 4. Run \`vendor/bin/phpstan analyse\` to run static analysis." . PHP_EOL;
if (!$noExceptions && $projectName !== null) {
echo " 5. Review copied exceptions in $exceptionsPath/ and adjust if needed." . PHP_EOL;
}
9 changes: 9 additions & 0 deletions config/exceptions/AccessDeniedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace {{ProjectName}}\Common\Exception;

final class AccessDeniedException extends DomainException implements AccessDeniedExceptionInterface
{
}
9 changes: 9 additions & 0 deletions config/exceptions/AccessDeniedExceptionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace {{ProjectName}}\Common\Exception;

interface AccessDeniedExceptionInterface extends ClientErrorExceptionInterface
{
}
11 changes: 11 additions & 0 deletions config/exceptions/ClientErrorExceptionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace {{ProjectName}}\Common\Exception;

use Throwable;

interface ClientErrorExceptionInterface extends Throwable
{
}
11 changes: 11 additions & 0 deletions config/exceptions/ConfigurationException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace {{ProjectName}}\Common\Exception;

use LogicException;

final class ConfigurationException extends LogicException implements ConfigurationExceptionInterface
{
}
9 changes: 9 additions & 0 deletions config/exceptions/ConfigurationExceptionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace {{ProjectName}}\Common\Exception;

interface ConfigurationExceptionInterface extends ServerErrorExceptionInterface
{
}
12 changes: 12 additions & 0 deletions config/exceptions/ConflictException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace {{ProjectName}}\Common\Exception;

/**
* @SuppressWarnings(PHPMD.DepthOfInheritance)
*/
final class ConflictException extends ValidationException implements ConflictExceptionInterface
{
}
9 changes: 9 additions & 0 deletions config/exceptions/ConflictExceptionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace {{ProjectName}}\Common\Exception;

interface ConflictExceptionInterface extends ClientErrorExceptionInterface
{
}
9 changes: 9 additions & 0 deletions config/exceptions/DomainException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace {{ProjectName}}\Common\Exception;

class DomainException extends \DomainException implements DomainExceptionInterface
{
}
11 changes: 11 additions & 0 deletions config/exceptions/DomainExceptionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace {{ProjectName}}\Common\Exception;

use Throwable;

interface DomainExceptionInterface extends Throwable
{
}
11 changes: 11 additions & 0 deletions config/exceptions/InfrastructureException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace {{ProjectName}}\Common\Exception;

use RuntimeException;

final class InfrastructureException extends RuntimeException implements InfrastructureExceptionInterface
{
}
9 changes: 9 additions & 0 deletions config/exceptions/InfrastructureExceptionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace {{ProjectName}}\Common\Exception;

interface InfrastructureExceptionInterface extends ServerErrorExceptionInterface
{
}
9 changes: 9 additions & 0 deletions config/exceptions/NotFoundException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace {{ProjectName}}\Common\Exception;

final class NotFoundException extends DomainException implements NotFoundExceptionInterface
{
}
9 changes: 9 additions & 0 deletions config/exceptions/NotFoundExceptionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace {{ProjectName}}\Common\Exception;

interface NotFoundExceptionInterface extends ClientErrorExceptionInterface
{
}
11 changes: 11 additions & 0 deletions config/exceptions/ServerErrorExceptionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace {{ProjectName}}\Common\Exception;

use Throwable;

interface ServerErrorExceptionInterface extends Throwable
{
}
12 changes: 12 additions & 0 deletions config/exceptions/ValidationException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace {{ProjectName}}\Common\Exception;

/**
* @SuppressWarnings(PHPMD.NumberOfChildren)
*/
class ValidationException extends DomainException implements ValidationExceptionInterface
{
}
9 changes: 9 additions & 0 deletions config/exceptions/ValidationExceptionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace {{ProjectName}}\Common\Exception;

interface ValidationExceptionInterface extends ClientErrorExceptionInterface
{
}
Loading