docs: consolidate documentation into organized structure

- Move 12 markdown files from root to docs/ subdirectories
- Organize documentation by category:
  • docs/troubleshooting/ (1 file)  - Technical troubleshooting guides
  • docs/deployment/      (4 files) - Deployment and security documentation
  • docs/guides/          (3 files) - Feature-specific guides
  • docs/planning/        (4 files) - Planning and improvement proposals

Root directory cleanup:
- Reduced from 16 to 4 markdown files in root
- Only essential project files remain:
  • CLAUDE.md (AI instructions)
  • README.md (Main project readme)
  • CLEANUP_PLAN.md (Current cleanup plan)
  • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements)

This improves:
 Documentation discoverability
 Logical organization by purpose
 Clean root directory
 Better maintainability
This commit is contained in:
2025-10-05 11:05:04 +02:00
parent 887847dde6
commit 5050c7d73a
36686 changed files with 196456 additions and 12398919 deletions

View File

@@ -0,0 +1,104 @@
<?php
declare(strict_types=1);
use App\Framework\Router\ValueObjects\Placeholder;
describe('Placeholder', function () {
describe('creation', function () {
it('can be created from string', function () {
$placeholder = Placeholder::fromString('id');
expect($placeholder->getName())->toBe('id');
expect($placeholder->toString())->toBe('{id}');
expect($placeholder->isWildcard())->toBeFalse();
});
it('can be created with type', function () {
$placeholder = Placeholder::typed('id', 'int');
expect($placeholder->getName())->toBe('id');
expect($placeholder->getType())->toBe('int');
expect($placeholder->getPattern())->toBe('(\d+)');
});
it('can be created as wildcard', function () {
$placeholder = Placeholder::wildcard('path');
expect($placeholder->getName())->toBe('path');
expect($placeholder->toString())->toBe('{path*}');
expect($placeholder->isWildcard())->toBeTrue();
expect($placeholder->getPattern())->toBe('(.+?)');
});
it('validates placeholder names', function () {
expect(fn() => Placeholder::fromString(''))
->toThrow(InvalidArgumentException::class);
expect(fn() => Placeholder::fromString('123invalid'))
->toThrow(InvalidArgumentException::class);
expect(fn() => Placeholder::fromString('invalid-name'))
->toThrow(InvalidArgumentException::class);
});
it('accepts valid placeholder names', function () {
$validNames = ['id', 'userId', 'user_id', '_private', 'snake_case'];
foreach ($validNames as $name) {
$placeholder = Placeholder::fromString($name);
expect($placeholder->getName())->toBe($name);
}
});
});
describe('type patterns', function () {
it('provides correct patterns for common types', function () {
$patterns = [
'int' => '(\d+)',
'uuid' => '([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})',
'slug' => '([a-z0-9\-]+)',
'alpha' => '([a-zA-Z]+)',
'alphanumeric' => '([a-zA-Z0-9]+)',
'filename' => '([a-zA-Z0-9._\-]+)',
];
foreach ($patterns as $type => $expectedPattern) {
$placeholder = Placeholder::typed('test', $type);
expect($placeholder->getPattern())->toBe($expectedPattern);
}
});
it('falls back to default pattern for unknown types', function () {
$placeholder = Placeholder::typed('test', 'unknown');
expect($placeholder->getPattern())->toBe('([^/]+)');
});
it('allows custom patterns', function () {
$placeholder = Placeholder::typed('test', 'custom', '([a-z]{3})');
expect($placeholder->getPattern())->toBe('([a-z]{3})');
});
});
describe('string representation', function () {
it('converts to proper placeholder syntax', function () {
$regular = Placeholder::fromString('id');
$wildcard = Placeholder::wildcard('path');
expect($regular->toString())->toBe('{id}');
expect($wildcard->toString())->toBe('{path*}');
});
});
describe('pattern generation', function () {
it('returns correct regex patterns', function () {
$regular = Placeholder::fromString('id');
$wildcard = Placeholder::wildcard('path');
$typed = Placeholder::typed('id', 'int');
expect($regular->getPattern())->toBe('([^/]+)');
expect($wildcard->getPattern())->toBe('(.+?)');
expect($typed->getPattern())->toBe('(\d+)');
});
});
});

View File

@@ -0,0 +1,158 @@
<?php
declare(strict_types=1);
use App\Framework\Router\ValueObjects\Placeholder;
use App\Framework\Router\ValueObjects\RoutePath;
describe('RoutePath', function () {
describe('creation', function () {
it('can be created from elements', function () {
$path = RoutePath::fromElements('api', 'users', Placeholder::fromString('id'));
expect($path->toString())->toBe('/api/users/{id}');
expect($path->isDynamic())->toBeTrue();
expect($path->getParameterNames())->toBe(['id']);
});
it('can be created from string', function () {
$path = RoutePath::fromString('/api/users/{id}');
expect($path->toString())->toBe('/api/users/{id}');
expect($path->isDynamic())->toBeTrue();
expect($path->getParameterNames())->toBe(['id']);
});
it('handles static routes', function () {
$path = RoutePath::fromElements('api', 'health');
expect($path->toString())->toBe('/api/health');
expect($path->isStatic())->toBeTrue();
expect($path->getParameterNames())->toBe([]);
});
it('handles wildcard parameters', function () {
$path = RoutePath::fromString('/files/{path*}');
expect($path->toString())->toBe('/files/{path*}');
$placeholders = $path->getPlaceholders();
expect($placeholders)->toHaveCount(1);
expect($placeholders[0]->isWildcard())->toBeTrue();
});
it('throws on empty path', function () {
expect(fn() => RoutePath::fromElements())
->toThrow(InvalidArgumentException::class);
});
});
describe('fluent builder', function () {
it('can build paths fluently', function () {
$path = RoutePath::create()
->segment('api')
->segment('users')
->parameter('id')
->build();
expect($path->toString())->toBe('/api/users/{id}');
});
it('supports typed parameters', function () {
$path = RoutePath::create()
->segment('api')
->segment('users')
->typedParameter('id', 'uuid')
->build();
$placeholders = $path->getPlaceholders();
expect($placeholders[0]->getType())->toBe('uuid');
});
it('supports quick patterns', function () {
$path = RoutePath::create()
->segment('api')
->segment('users')
->uuid();
expect($path->toString())->toBe('/api/users/{id}');
});
});
describe('regex compilation', function () {
it('compiles static routes to regex', function () {
$path = RoutePath::fromString('/api/health');
expect($path->toRegex())->toBe('~^/api/health$~');
});
it('compiles dynamic routes to regex', function () {
$path = RoutePath::fromString('/api/users/{id}');
expect($path->toRegex())->toBe('~^/api/users/([^/]+)$~');
});
it('compiles typed parameters correctly', function () {
$path = RoutePath::fromElements(
'api',
'users',
Placeholder::typed('id', 'int')
);
expect($path->toRegex())->toBe('~^/api/users/(\d+)$~');
});
it('compiles wildcard parameters', function () {
$path = RoutePath::fromElements(
'files',
Placeholder::wildcard('path')
);
expect($path->toRegex())->toBe('~^/files/(.+?)$~');
});
});
describe('manipulation', function () {
it('can append elements', function () {
$base = RoutePath::fromElements('api', 'users');
$extended = $base->append(Placeholder::fromString('id'));
expect($extended->toString())->toBe('/api/users/{id}');
});
it('can prepend elements', function () {
$base = RoutePath::fromElements('users', Placeholder::fromString('id'));
$extended = $base->prepend('api');
expect($extended->toString())->toBe('/api/users/{id}');
});
});
describe('analysis', function () {
it('counts segments correctly', function () {
$path = RoutePath::fromString('/api/users/{id}');
expect($path->getSegmentCount())->toBe(3);
});
it('identifies placeholders', function () {
$path = RoutePath::fromString('/api/users/{id}/posts/{postId}');
$placeholders = $path->getPlaceholders();
expect($placeholders)->toHaveCount(2);
expect($path->getParameterNames())->toBe(['id', 'postId']);
});
});
describe('validation', function () {
it('validates segment content', function () {
expect(fn() => RoutePath::fromElements('api', ''))
->toThrow(InvalidArgumentException::class);
});
it('rejects invalid characters in segments', function () {
expect(fn() => RoutePath::fromElements('api', 'users{id}'))
->toThrow(InvalidArgumentException::class);
});
});
});