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

@@ -29,7 +29,9 @@ describe('DiscoveryService Integration with Console Commands', function () {
$serializer = new PhpSerializer(PhpSerializerConfig::safe());
$this->cache = new GeneralCache($cacheDriver, $serializer);
$this->clock = new SystemClock();
$this->pathProvider = new PathProvider('/home/michael/dev/michaelschiemer');
// Use correct base path for Docker environment
$basePath = file_exists('/var/www/html/src') ? '/var/www/html' : '/home/michael/dev/michaelschiemer';
$this->pathProvider = new PathProvider($basePath);
// Register dependencies in container
$this->container->singleton(Cache::class, $this->cache);
@@ -228,7 +230,9 @@ describe('DiscoveryService Performance and Error Handling', function () {
$serializer = new PhpSerializer(PhpSerializerConfig::safe());
$this->cache = new GeneralCache($cacheDriver, $serializer);
$this->clock = new SystemClock();
$this->pathProvider = new PathProvider('/home/michael/dev/michaelschiemer');
// Use correct base path for Docker environment
$basePath = file_exists('/var/www/html/src') ? '/var/www/html' : '/home/michael/dev/michaelschiemer';
$this->pathProvider = new PathProvider($basePath);
$this->container->singleton(Cache::class, $this->cache);
$this->container->singleton(Clock::class, $this->clock);

View File

@@ -0,0 +1,198 @@
<?php
declare(strict_types=1);
use App\Framework\Discovery\Processing\ClassExtractor;
use App\Framework\Filesystem\File;
use App\Framework\Filesystem\FilePath;
use App\Framework\Filesystem\FileSystemService;
beforeEach(function () {
$this->fileSystem = new FileSystemService();
$this->extractor = new ClassExtractor($this->fileSystem);
$this->tmpDir = '/var/www/html/tests/tmp';
if (!is_dir($this->tmpDir)) {
mkdir($this->tmpDir, 0777, true);
}
$this->createTestFile = function (string $filename, string $content): File {
$filepath = $this->tmpDir . '/' . $filename;
file_put_contents($filepath, $content);
$spl = new SplFileInfo($filepath);
return File::fromSplFileInfo($spl);
};
});
afterEach(function () {
// Clean up test files
if (is_dir($this->tmpDir)) {
$files = glob($this->tmpDir . '/*');
foreach ($files as $file) {
if (is_file($file)) {
unlink($file);
}
}
}
});
describe('ClassExtractor', function () {
it('extracts classes from PHP file', function () {
$content = <<<'PHP'
<?php
namespace App\Test;
final readonly class TestClass
{
public function test(): void {}
}
PHP;
$file = ($this->createTestFile)('TestClass.php', $content);
$classes = $this->extractor->extractFromFile($file);
expect($classes)->toHaveCount(1);
expect($classes[0]->getFullyQualified())->toBe('App\Test\TestClass');
});
it('returns empty array for HTML template files without PHP tags', function () {
$content = <<<'HTML'
<layout name="layouts/main" />
<div class="section">
<h2>Dashboard</h2>
<div class="stats-grid">
<div class="stat-card">
<h3>Status</h3>
</div>
</div>
</div>
HTML;
$file = ($this->createTestFile)('template.view.php', $content);
$classes = $this->extractor->extractFromFile($file);
expect($classes)->toHaveCount(0);
});
it('extracts multiple classes from file', function () {
$content = <<<'PHP'
<?php
namespace App\Test;
interface TestInterface {}
final readonly class TestClass implements TestInterface {}
trait TestTrait {}
enum TestEnum {
case CASE_ONE;
}
PHP;
$file = ($this->createTestFile)('Multiple.php', $content);
$classes = $this->extractor->extractFromFile($file);
expect($classes)->toHaveCount(4);
});
it('handles files with short PHP tags', function () {
$content = <<<'PHP'
<?= "test" ?>
<?php
class ShortTagTest {}
PHP;
$file = ($this->createTestFile)('ShortTag.php', $content);
$classes = $this->extractor->extractFromFile($file);
expect($classes)->toHaveCount(1);
});
it('extracts detailed class information', function () {
$content = <<<'PHP'
<?php
namespace App\Test;
final readonly class DetailedClass
{
public function method(): void {}
}
PHP;
$file = ($this->createTestFile)('Detailed.php', $content);
$detailed = $this->extractor->extractDetailedFromFile($file);
expect($detailed)->toHaveCount(1);
expect($detailed[0]['type'])->toBe('class');
expect($detailed[0]['name'])->toBe('DetailedClass');
expect($detailed[0]['namespace'])->toBe('App\Test');
expect($detailed[0]['fqn'])->toBe('App\Test\DetailedClass');
});
it('returns empty array for files with syntax errors', function () {
$content = <<<'PHP'
<?php
class InvalidClass {
// Missing closing brace
PHP;
$file = ($this->createTestFile)('Invalid.php', $content);
$classes = $this->extractor->extractFromFile($file);
expect($classes)->toHaveCount(0);
});
it('analyzes complete file structure', function () {
$content = <<<'PHP'
<?php
namespace App\Test;
use App\Framework\Core\Application;
final readonly class AnalysisTest
{
public function __construct(
private Application $app
) {}
}
PHP;
$file = ($this->createTestFile)('Analysis.php', $content);
$analysis = $this->extractor->analyzeFile($file);
expect($analysis)->toHaveKeys(['classes', 'functions', 'attributes', 'uses']);
expect($analysis['classes'])->toHaveCount(1);
expect($analysis['uses'])->toHaveCount(1);
});
it('does not extract from pure HTML without PHP tags', function () {
$content = <<<'HTML'
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<div class="container">
<h1>Title</h1>
</div>
</body>
</html>
HTML;
$file = ($this->createTestFile)('pure.html', $content);
$classes = $this->extractor->extractFromFile($file);
expect($classes)->toHaveCount(0);
});
});

View File

@@ -0,0 +1,194 @@
<?php
declare(strict_types=1);
namespace Tests\Framework\Discovery;
use App\Framework\Cache\Cache;
use App\Framework\Cache\Driver\InMemoryCache;
use App\Framework\Cache\GeneralCache;
use App\Framework\Core\PathProvider;
use App\Framework\DateTime\SystemClock;
use App\Framework\Discovery\Results\DiscoveryRegistry;
use App\Framework\Discovery\UnifiedDiscoveryService;
use App\Framework\Discovery\ValueObjects\DiscoveryConfiguration;
use App\Framework\Reflection\CachedReflectionProvider;
use App\Framework\Serializer\Php\PhpSerializer;
use App\Framework\Serializer\Php\PhpSerializerConfig;
describe('UnifiedDiscoveryService', function () {
beforeEach(function () {
$cacheDriver = new InMemoryCache();
$serializer = new PhpSerializer(PhpSerializerConfig::safe());
$this->cache = new GeneralCache($cacheDriver, $serializer);
$this->clock = new SystemClock();
$this->pathProvider = new PathProvider('/home/michael/dev/michaelschiemer');
$this->reflectionProvider = new CachedReflectionProvider();
$this->configuration = new DiscoveryConfiguration(
paths: ['/home/michael/dev/michaelschiemer/tests/fixtures'],
useCache: false, // Disable cache for tests
enableMemoryMonitoring: false,
memoryLimitMB: 64,
maxFilesPerBatch: 100
);
// Clear cache between tests
$this->cache->clear();
});
it('can be instantiated with required dependencies', function () {
$service = new UnifiedDiscoveryService(
pathProvider: $this->pathProvider,
cache: $this->cache,
clock: $this->clock,
reflectionProvider: $this->reflectionProvider,
configuration: $this->configuration
);
expect($service)->toBeInstanceOf(UnifiedDiscoveryService::class);
});
it('performs discovery and returns DiscoveryRegistry', function () {
$service = new UnifiedDiscoveryService(
pathProvider: $this->pathProvider,
cache: $this->cache,
clock: $this->clock,
reflectionProvider: $this->reflectionProvider,
configuration: $this->configuration
);
$registry = $service->discover();
expect($registry)->toBeInstanceOf(DiscoveryRegistry::class);
expect($registry->attributes)->toBeInstanceOf(\App\Framework\Discovery\Results\AttributeRegistry::class);
expect($registry->interfaces)->toBeInstanceOf(\App\Framework\Discovery\Results\InterfaceRegistry::class);
});
it('handles empty scan paths gracefully', function () {
$emptyConfig = new DiscoveryConfiguration(
paths: [],
useCache: false,
enableMemoryMonitoring: false,
memoryLimitMB: 64,
maxFilesPerBatch: 100
);
$service = new UnifiedDiscoveryService(
pathProvider: $this->pathProvider,
cache: $this->cache,
clock: $this->clock,
reflectionProvider: $this->reflectionProvider,
configuration: $emptyConfig
);
$registry = $service->discover();
expect($registry)->toBeInstanceOf(DiscoveryRegistry::class);
// Empty paths should still return a valid registry, just empty
expect($registry->attributes->getAll())->toBeArray();
});
it('supports incremental discovery', function () {
$service = new UnifiedDiscoveryService(
pathProvider: $this->pathProvider,
cache: $this->cache,
clock: $this->clock,
reflectionProvider: $this->reflectionProvider,
configuration: $this->configuration
);
$registry = $service->incrementalDiscover();
expect($registry)->toBeInstanceOf(DiscoveryRegistry::class);
});
it('validates configuration on instantiation', function () {
$invalidConfig = new DiscoveryConfiguration(
paths: ['/nonexistent/path'],
useCache: false,
enableMemoryMonitoring: false,
memoryLimitMB: -1, // Invalid memory limit
maxFilesPerBatch: 0 // Invalid batch size
);
expect(function () {
new UnifiedDiscoveryService(
pathProvider: $this->pathProvider,
cache: $this->cache,
clock: $this->clock,
reflectionProvider: $this->reflectionProvider,
configuration: $invalidConfig
);
})->toThrow(\InvalidArgumentException::class);
});
it('handles memory management configuration', function () {
$memoryConfig = new DiscoveryConfiguration(
paths: ['/home/michael/dev/michaelschiemer/src'],
useCache: false,
enableMemoryMonitoring: true,
memoryLimitMB: 32, // 32MB limit
maxFilesPerBatch: 50
);
$service = new UnifiedDiscoveryService(
pathProvider: $this->pathProvider,
cache: $this->cache,
clock: $this->clock,
reflectionProvider: $this->reflectionProvider,
configuration: $memoryConfig
);
expect($service)->toBeInstanceOf(UnifiedDiscoveryService::class);
// Should not throw memory errors with management enabled
$registry = $service->discover();
expect($registry)->toBeInstanceOf(DiscoveryRegistry::class);
});
it('respects chunk size in configuration', function () {
$smallChunkConfig = new DiscoveryConfiguration(
paths: ['/home/michael/dev/michaelschiemer/tests'],
useCache: false,
enableMemoryMonitoring: false,
memoryLimitMB: 64,
maxFilesPerBatch: 5 // Very small batches
);
$service = new UnifiedDiscoveryService(
pathProvider: $this->pathProvider,
cache: $this->cache,
clock: $this->clock,
reflectionProvider: $this->reflectionProvider,
configuration: $smallChunkConfig
);
$registry = $service->discover();
expect($registry)->toBeInstanceOf(DiscoveryRegistry::class);
// Should handle small chunks without issues
});
it('handles file system errors gracefully', function () {
$invalidPathConfig = new DiscoveryConfiguration(
paths: ['/completely/nonexistent/path'],
useCache: false,
enableMemoryMonitoring: false,
memoryLimitMB: 64,
maxFilesPerBatch: 100
);
$service = new UnifiedDiscoveryService(
pathProvider: $this->pathProvider,
cache: $this->cache,
clock: $this->clock,
reflectionProvider: $this->reflectionProvider,
configuration: $invalidPathConfig
);
// Should not throw but return empty registry
$registry = $service->discover();
expect($registry)->toBeInstanceOf(DiscoveryRegistry::class);
});
});