diff --git a/.cliffignore b/.cliffignore deleted file mode 100644 index 187668fd1..000000000 --- a/.cliffignore +++ /dev/null @@ -1,7 +0,0 @@ -9766c534bddad8e82e6d19f9bad5cf70b9887f9a -92ce77ec0ec703c08a659419087a373f76e711f7 -2d53efc945c7747be1755d0b66557a86bdc12cbd -602137b65129b817811b80975a369ebde3270c6d -4eb26ae37e1f4c82a45961517ffeb54c20200408 -e59adce848a9e10ee5775254045cbbd915236b8b -9e0a64108d62236ab07b3f8d10e8c78269b8e1d1 diff --git a/.github/ISSUE_TEMPLATE/feature---enhancement-request.md b/.github/ISSUE_TEMPLATE/feature---enhancement-request.md deleted file mode 100644 index 9f68fc3a6..000000000 --- a/.github/ISSUE_TEMPLATE/feature---enhancement-request.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: Feature / Enhancement request -about: Suggest an idea for TorrentPier -title: "[Feature]" -labels: [Feature, Enhancement] -assignees: '' ---- diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml deleted file mode 100644 index 8e256db33..000000000 --- a/.github/workflows/cd.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: Continuous Deployment - -on: - push: - tags: - - "v*.*.*" - -jobs: - generate-changelog: - name: Generate changelog - runs-on: ubuntu-22.04 - outputs: - release_body: ${{ steps.git-cliff.outputs.content }} - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Generate a changelog - uses: orhun/git-cliff-action@v4 - id: git-cliff - with: - config: cliff.toml - args: -vv --latest --no-exec --github-repo ${{ github.repository }} - - - name: Print the changelog - run: cat "${{ steps.git-cliff.outputs.changelog }}" - - release: - name: Create release - needs: [ generate-changelog ] - runs-on: ubuntu-22.04 - - steps: - - uses: actions/checkout@v4 - - name: Set the release version - shell: bash - run: echo "RELEASE_VERSION=${GITHUB_REF:11}" >> $GITHUB_ENV - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '8.3' - - - name: Install Composer dependencies - run: composer install --no-dev --no-progress --prefer-dist --optimize-autoloader - - - name: Cleanup - run: php _cleanup.php && rm _cleanup.php - - - name: Create archive - id: create-zip - run: | - ZIP_NAME="torrentpier-v${{ env.RELEASE_VERSION }}.zip" - zip -r "$ZIP_NAME" . -x ".git/*" - echo "ZIP_NAME=$ZIP_NAME" >> $GITHUB_OUTPUT - - - name: Publish to GitHub - if: ${{ !contains(github.ref, '-') }} - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{ steps.create-zip.outputs.ZIP_NAME }} - overwrite: true - tag: ${{ github.ref }} - release_name: "v${{ env.RELEASE_VERSION }}" - body: "${{ needs.generate-changelog.outputs.release_body }}" - - - name: Publish to GitHub (pre-release) - if: ${{ contains(github.ref, '-') }} - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{ steps.create-zip.outputs.ZIP_NAME }} - overwrite: true - tag: ${{ github.ref }} - release_name: "v${{ env.RELEASE_VERSION }}" - body: "${{ needs.generate-changelog.outputs.release_body }}" - prerelease: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 424e53a1f..000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,74 +0,0 @@ -name: Continuous Integration - -on: - push: - branches: - - master - -jobs: - nightly: - name: Nightly builds ๐Ÿ“ฆ - runs-on: ubuntu-22.04 - - steps: - - name: Checkout code ๐Ÿ—ณ - uses: actions/checkout@v4 - - - name: Setup PHP ๐Ÿ”ฉ - uses: shivammathur/setup-php@v2 - with: - php-version: '8.3' - - - name: Install Composer dependencies ๐Ÿชš - run: composer install --no-dev --no-progress --prefer-dist --optimize-autoloader - - - name: Get commit hash ๐Ÿ”— - id: get-commit-hash - run: | - COMMIT_HASH=$(git rev-parse --short HEAD) - echo "COMMIT_HASH=$COMMIT_HASH" >> $GITHUB_OUTPUT - - - name: Cleanup - run: php _cleanup.php && rm _cleanup.php - - - name: Create archive ๐Ÿ—ž - id: create-zip - run: | - ZIP_NAME="torrentpier-${{ steps.get-commit-hash.outputs.COMMIT_HASH }}.zip" - zip -r "$ZIP_NAME" . -x ".git/*" - echo "ZIP_NAME=$ZIP_NAME" >> $GITHUB_OUTPUT - - - name: Upload Archive ๐Ÿ“ค - uses: actions/upload-artifact@v4 - with: - name: TorrentPier-master - path: ${{ steps.create-zip.outputs.ZIP_NAME }} - - deploy: - name: ๐ŸŽ‰ Deploy - runs-on: ubuntu-22.04 - steps: - - name: ๐Ÿšš Get latest code - uses: actions/checkout@v4 - - - name: ๐Ÿ”ฉ Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '8.3' - - - name: ๐Ÿ–‡ Install Composer dependencies - run: composer install --no-dev --no-progress --prefer-dist --optimize-autoloader - - - name: ๐Ÿ“‚ Sync files - uses: SamKirkland/FTP-Deploy-Action@v4.3.5 - with: - server: ${{ secrets.FTP_SERVER }} - username: ${{ secrets.FTP_USERNAME }} - password: ${{ secrets.FTP_PASSWORD }} - server-dir: ${{ secrets.FTP_DIR }} - protocol: ${{ secrets.FTP_PROTOCOL }} - port: ${{ secrets.FTP_PORT }} - exclude: | - **/.git* - **/.git*/** - .env diff --git a/.github/workflows/phpmd.yml b/.github/workflows/phpmd.yml deleted file mode 100644 index 3e06d7538..000000000 --- a/.github/workflows/phpmd.yml +++ /dev/null @@ -1,57 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. -# PHPMD is a spin-off project of PHP Depend and -# aims to be a PHP equivalent of the well known Java tool PMD. -# What PHPMD does is: It takes a given PHP source code base -# and look for several potential problems within that source. -# These problems can be things like: -# Possible bugs -# Suboptimal code -# Overcomplicated expressions -# Unused parameters, methods, properties -# More details at https://phpmd.org/ - -name: PHPMD - -on: - push: - branches: [ "master" ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ "master" ] - schedule: - - cron: '40 0 * * 3' - -permissions: - contents: read - -jobs: - PHPMD: - name: Run PHPMD scanning - runs-on: ubuntu-latest - permissions: - contents: read # for checkout to fetch code - security-events: write # for github/codeql-action/upload-sarif to upload SARIF results - actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup PHP - uses: shivammathur/setup-php@aa1fe473f9c687b6fb896056d771232c0bc41161 - with: - coverage: none - tools: phpmd - - - name: Run PHPMD - run: phpmd . sarif codesize --reportfile phpmd-results.sarif - continue-on-error: true - - - name: Upload analysis results to GitHub - uses: github/codeql-action/upload-sarif@v2 - with: - sarif_file: phpmd-results.sarif - wait-for-processing: true diff --git a/.github/workflows/schedule.yml b/.github/workflows/schedule.yml deleted file mode 100644 index c1ad4f3c1..000000000 --- a/.github/workflows/schedule.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Changelog generation - -on: - schedule: - - cron: '0 0 * * *' - workflow_dispatch: - -jobs: - changelog: - name: Changelog generation - runs-on: ubuntu-22.04 - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: master - token: ${{ secrets.REPO_TOKEN }} - - - name: Generate a changelog - uses: orhun/git-cliff-action@v4 - id: git-cliff - with: - config: cliff.toml - args: v2.4.6-alpha.4.. --verbose - env: - OUTPUT: CHANGELOG.md - GITHUB_REPO: ${{ github.repository }} - - - name: Print the changelog - run: cat "${{ steps.git-cliff.outputs.changelog }}" - - - name: Commit changelog - run: | - git checkout master - git config --local user.name 'belomaxorka' - git config --local user.email 'roman25052006.kelesh@gmail.com' - set +e - git add CHANGELOG.md - git commit -m "changelog: Update CHANGELOG.md ๐Ÿ“–" - git push https://${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git master diff --git a/.htaccess b/.htaccess deleted file mode 100644 index a689fba84..000000000 --- a/.htaccess +++ /dev/null @@ -1,18 +0,0 @@ -## set default server charset -AddDefaultCharset UTF-8 - -## folder listing access control -Options All -Indexes - -## sitemap and atom rewrite -RewriteEngine On -RewriteRule ^sitemap.xml$ sitemap/sitemap.xml [L] -RewriteRule ^/internal_data/atom/(.*) /atom$1 [L] - -## deny access to git folder -RedirectMatch 404 /\\.git(/|$) - -## deny access to system files - -Require all denied - diff --git a/_cleanup.php b/_cleanup.php deleted file mode 100644 index d9802822a..000000000 --- a/_cleanup.php +++ /dev/null @@ -1,57 +0,0 @@ -php ' . basename(__FILE__) . ' in CLI mode'); -} - -// Get all constants -require_once BB_ROOT . 'library/defines.php'; - -// Include CLI functions -require INC_DIR . '/functions_cli.php'; - -// Welcoming message -out("--- Release creation tool ---\n", 'info'); - -$configFile = BB_PATH . '/library/config.php'; - -if (!is_file($configFile)) { - out('- Config file ' . basename($configFile) . ' not found', 'error'); - exit; -} -if (!is_readable($configFile)) { - out('- Config file ' . basename($configFile) . ' is not readable', 'error'); - exit; -} -if (!is_writable($configFile)) { - out('- Config file ' . basename($configFile) . ' is not writable', 'error'); - exit; -} - -// Ask for version -fwrite(STDOUT, 'Enter version number (e.g, v2.4.0): '); -$version = trim(fgets(STDIN)); - -if (empty($version)) { - out("- Version cannot be empty. Please enter a valid version number", 'error'); - exit; -} else { - // Add 'v' prefix if missing - if (!str_starts_with($version, 'v')) { - $version = 'v' . $version; - } - - out("- Using version: $version", 'info'); -} - -// Ask for version emoji -fwrite(STDOUT, 'Enter version emoji: '); -$versionEmoji = trim(fgets(STDIN)); - -if (!empty($versionEmoji)) { - out("- Using version emoji: $versionEmoji", 'info'); -} - -// Ask for release date or use today's date -fwrite(STDOUT, "Enter release date (e.g. 25-05-2025), leave empty to use today's date: "); -$date = trim(fgets(STDIN)); - -if (empty($date)) { - $date = date('d-m-Y'); - out("- Using current date: $date", 'info'); -} else { - // Validate date format (dd-mm-yyyy) - $dateObj = DateTime::createFromFormat('d-m-Y', $date); - if (!$dateObj || $dateObj->format('d-m-Y') !== $date) { - out("- Invalid date format. Expected format: DD-MM-YYYY", 'error'); - exit; - } - - out("- Using date: $date", 'info'); -} - -// Read config file content -$content = file_get_contents($configFile); - -// Update version -$content = preg_replace( - "/\\\$bb_cfg\['tp_version'\]\s*=\s*'[^']*';/", - "\$bb_cfg['tp_version'] = '$version';", - $content -); - -// Update release date -$content = preg_replace( - "/\\\$bb_cfg\['tp_release_date'\]\s*=\s*'[^']*';/", - "\$bb_cfg['tp_release_date'] = '$date';", - $content -); - -// Save updated config -$bytesWritten = file_put_contents($configFile, $content); - -if ($bytesWritten === false) { - out("- Failed to write to config file", 'error'); - exit; -} - -if ($bytesWritten === 0) { - out("- Config file was not updated (0 bytes written)", 'error'); - exit; -} - -out("\n- Config file has been updated!", 'success'); - -// Update CHANGELOG.md -runProcess('npx git-cliff v2.4.6-alpha.4.. --config cliff.toml --tag "' . $version . '" > CHANGELOG.md'); - -// Git add & commit -runProcess('git add -A && git commit -m "release: ' . escapeshellarg($version) . (!empty($versionEmoji) ? (' ' . $versionEmoji) : '') . '"'); - -// Git tag -runProcess("git tag -a \"$version\" -m \"Release $version\""); -runProcess("git tag -v \"$version\""); - -// Git push -runProcess("git push origin master"); -runProcess("git push origin $version"); - -out("\n- Release $version has been successfully prepared, committed and pushed!", 'success'); diff --git a/cliff.toml b/cliff.toml deleted file mode 100644 index 1798567f1..000000000 --- a/cliff.toml +++ /dev/null @@ -1,126 +0,0 @@ -# git-cliff ~ TorrentPier configuration file -# https://git-cliff.org/docs/configuration -# -# Lines starting with "#" are comments. -# Configuration options are organized into tables and keys. -# See documentation for more information on available options. - -[remote.github] -owner = "torrentpier" -repo = "torrentpier" - -[changelog] -# template for the changelog header -header = """ -[![TorrentPier](https://raw.githubusercontent.com/torrentpier/.github/refs/heads/main/versions/Cattle.png)](https://github.com/torrentpier)\n -# ๐Ÿ“– Change Log\n -""" -# template for the changelog body -# https://keats.github.io/tera/docs/#introduction -body = """ -{%- macro remote_url() -%} - https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} -{%- endmacro -%} - -{%- macro nightly_url() -%} - https://nightly.link/{{ remote.github.owner }}/{{ remote.github.repo }}/workflows/ci/master/TorrentPier-master -{%- endmacro -%} - -{% macro print_commit(commit) -%} - - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ - {% if commit.breaking %}[**breaking**] {% endif %}\ - {{ commit.message | upper_first }} - \ - ([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\ -{% endmacro -%} - -{% if version %}\ - {% if previous.version %}\ - ## [{{ version }}]\ - ({{ self::remote_url() }}/compare/{{ previous.version }}..{{ version }}) ({{ timestamp | date(format="%Y-%m-%d") }}) - {% else %}\ - ## {{ version }} ({{ timestamp | date(format="%Y-%m-%d") }}) - {% endif %}\ -{% else %}\ - ## [nightly]({{ self::nightly_url() }}) -{% endif %}\ - -{% for group, commits in commits | group_by(attribute="group") %} - ### {{ group | striptags | trim | upper_first }} - {% for commit in commits - | filter(attribute="scope") - | sort(attribute="scope") %} - {{ self::print_commit(commit=commit) }} - {%- endfor %} - {% for commit in commits %} - {%- if not commit.scope -%} - {{ self::print_commit(commit=commit) }} - {% endif -%} - {% endfor -%} -{% endfor -%} -{%- if github -%} -{% if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %} - ## New Contributors โค๏ธ -{% endif %}\ -{% for contributor in github.contributors | filter(attribute="is_first_time", value=true) %} - * @{{ contributor.username }} made their first contribution - {%- if contributor.pr_number %} in \ - [#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \ - {%- endif %} -{%- endfor -%} -{%- endif %} - - -""" -# template for the changelog footer -footer = """ -""" -# remove the leading and trailing whitespace from the templates -trim = true -# postprocessors -postprocessors = [ - { pattern = '', replace = "https://github.com/torrentpier/torrentpier" }, # replace repository URL -] - -[git] -# parse the commits based on https://www.conventionalcommits.org -conventional_commits = true -# filter out the commits that are not conventional -filter_unconventional = true -# process each line of a commit as an individual commit -split_commits = false -# regex for preprocessing the commit messages -commit_preprocessors = [ - # Replace issue numbers - { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/pull/${2}))" }, - # Check spelling of the commit with https://github.com/crate-ci/typos - # If the spelling is incorrect, it will be automatically fixed. - # { pattern = '.*', replace_command = 'typos --write-changes -' }, -] -# regex for parsing and grouping commits -commit_parsers = [ - { message = "^feat", group = "๐Ÿš€ Features" }, - { message = "^fix", group = "๐Ÿ› Bug Fixes" }, - { message = "^doc", group = "๐Ÿ“š Documentation" }, - { message = "^perf", group = "โšก Performance" }, - { message = "^refactor", group = "๐Ÿšœ Refactor" }, - { message = "^style", group = "๐ŸŽจ Styling" }, - { message = "^test", group = "๐Ÿงช Testing" }, - { message = "^ignore|^release|^changelog", skip = true }, - { message = "^chore|^ci|^misc", group = "โš™๏ธ Miscellaneous" }, - { body = ".*security", group = "๐Ÿ›ก๏ธ Security" }, - { message = "^revert", group = "โ—€๏ธ Revert" }, - { message = "^crowdin|^crodwin", group = "๐Ÿˆณ New translations" }, # crowdin pulls supporting - { message = "^Composer", group = "๐Ÿ“ฆ Dependencies" }, # dependabot pulls supporting - { message = "^rem|^drop|^removed", group = "๐Ÿ—‘๏ธ Removed" }, - { message = ".*", group = "๐Ÿ’ผ Other" }, -] -# protect breaking changes from being skipped due to matching a skipping commit_parser -protect_breaking_commits = false -# filter out the commits that are not matched by commit parsers -filter_commits = false -# regex for matching git tags -tag_pattern = "v[0-9].*" -# sort the tags topologically -topo_order = false -# sort the commits inside sections by oldest/newest order -sort_commits = "newest" diff --git a/config/README.md b/config/README.md deleted file mode 100644 index 1d4ccad07..000000000 --- a/config/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# Application Configuration - -System configuration files using PHP arrays for type safety and IDE support: - -- **app.php**: Core application settings - - Site name, URL, timezone - - Debug mode, environment - - Feature flags and toggles - -- **database.php**: Database connection settings - - Multiple connection definitions - - Read/write splitting configuration - - Connection pooling settings - -- **cache.php**: Cache driver configurations - - Redis, Memcached, file-based settings - - TTL defaults per cache type - - Cache key prefixes - -- **tracker.php**: BitTorrent tracker settings - - Announce intervals - - Peer limits - - Ratio requirements - -- **environments/**: Environment-specific overrides - - Development, staging, production settings - - Local developer configurations - -Example database configuration: -```php - env('DB_CONNECTION', 'mysql'), - - 'connections' => [ - 'mysql' => [ - 'driver' => 'mysql', - 'host' => env('DB_HOST', '127.0.0.1'), - 'port' => env('DB_PORT', 3306), - 'database' => env('DB_DATABASE', 'tp'), - 'username' => env('DB_USERNAME', 'root'), - 'password' => env('DB_PASSWORD', ''), - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'options' => [ - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - PDO::ATTR_EMULATE_PREPARES => false, - ], - ], - ], -]; -``` \ No newline at end of file diff --git a/config/container.php b/config/container.php deleted file mode 100644 index cc72536ee..000000000 --- a/config/container.php +++ /dev/null @@ -1,26 +0,0 @@ - env('APP_ENV', 'development'), - 'debug' => env('APP_DEBUG', false), - - // Enable/disable features - 'autowiring' => true, - 'annotations' => false, - - // Compilation settings for production - 'compilation_dir' => __DIR__ . '/../internal_data/cache/container', - 'proxies_dir' => __DIR__ . '/../internal_data/cache/proxies', - - // Additional definition files to load - 'definition_files' => [ - // Add custom definition files here - // __DIR__ . '/services/custom.php', - ], - - // Container-specific settings - 'container' => [ - // Add any PHP-DI specific settings here - ], -]; diff --git a/config/environments/.keep b/config/environments/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/config/services.php b/config/services.php deleted file mode 100644 index 5070cdf57..000000000 --- a/config/services.php +++ /dev/null @@ -1,30 +0,0 @@ - factory(function () { - // $logger = new \Monolog\Logger('torrentpier'); - // $logger->pushHandler(new \Monolog\Handler\StreamHandler(__DIR__ . '/../internal_data/logs/app.log')); - // return $logger; - // }), - - // Configuration service example - // 'config' => factory(function () { - // return [ - // 'app' => require __DIR__ . '/app.php', - // 'database' => require __DIR__ . '/database.php', - // 'cache' => require __DIR__ . '/cache.php', - // ]; - // }), - - // Interface to implementation binding example - // 'ServiceInterface' => autowire('ConcreteService'), -]; diff --git a/docs/examples/di-container-usage.php b/docs/examples/di-container-usage.php deleted file mode 100644 index fcfc0bdf9..000000000 --- a/docs/examples/di-container-usage.php +++ /dev/null @@ -1,99 +0,0 @@ -has('some.service'); // Check if service exists - -// ===== PHASE 2: Domain Modeling (FUTURE) ===== - -// 3. Repository interfaces (when implemented in Domain layer) -// $userRepository = app('TorrentPier\Domain\User\Repository\UserRepositoryInterface'); -// $torrentRepository = app('TorrentPier\Domain\Tracker\Repository\TorrentRepositoryInterface'); -// $forumRepository = app('TorrentPier\Domain\Forum\Repository\ForumRepositoryInterface'); - -// ===== PHASE 3: Application Services (FUTURE) ===== - -// 4. Command/Query handlers (when implemented) -// $registerUserHandler = app('TorrentPier\Application\User\Handler\RegisterUserHandler'); -// $announceHandler = app('TorrentPier\Application\Tracker\Handler\ProcessAnnounceHandler'); -// $createPostHandler = app('TorrentPier\Application\Forum\Handler\CreatePostHandler'); - -// 5. Making command instances with parameters -// $command = $container->make('TorrentPier\Application\User\Command\RegisterUserCommand', [ -// 'username' => 'john_doe', -// 'email' => 'john@example.com', -// 'password' => 'secure_password' -// ]); - -// ===== PHASE 4: Infrastructure (FUTURE) ===== - -// 6. Database and cache (when infrastructure is implemented) -// $database = app('database.connection.default'); -// $cache = app('cache.factory')('forum'); // Get cache instance for 'forum' namespace - -// ===== PHASE 5: Presentation (FUTURE) ===== - -// 7. Controllers (when implemented) -// $userController = app('TorrentPier\Presentation\Http\Controllers\Api\UserController'); -// $trackerController = app('TorrentPier\Presentation\Http\Controllers\Web\TrackerController'); - -// ===== TESTING EXAMPLES ===== - -// 8. Testing with custom container (works now) -$testContainer = ContainerFactory::create([ - 'definitions' => [ - 'test.service' => \DI\factory(function () { - return new class { - public function test() { return 'test'; } - }; - }), - ], - 'environment' => 'testing', -]); - -// 9. Safe service resolution (works now) -try { - $service = app('optional.service'); -} catch (RuntimeException $e) { - // Service not found, handle gracefully - $service = null; -} - -// ===== LEGACY INTEGRATION (CURRENT) ===== - -// 10. Integration with legacy code -// In legacy files, after including common.php or similar: -if (!Bootstrap::getContainer()) { - Bootstrap::init(BB_ROOT ?? __DIR__ . '/../..'); -} - -// 11. Method injection (works now if service exists) -class ExampleService -{ - public function processData(string $data) - { - // Container can inject dependencies when calling this method - return "Processed: $data"; - } -} - -$exampleService = new ExampleService(); -$result = $container->call([$exampleService, 'processData'], [ - 'data' => 'test data' -]); \ No newline at end of file diff --git a/docs/specs/hexagonal-architecture-spec.md b/docs/specs/hexagonal-architecture-spec.md deleted file mode 100644 index 07dc0a672..000000000 --- a/docs/specs/hexagonal-architecture-spec.md +++ /dev/null @@ -1,436 +0,0 @@ -# Hexagonal Architecture Directory Structure Specification - -## Overview - -This document specifies the new hexagonal architecture directory structure for TorrentPier 3.0. The structure follows Domain-Driven Design (DDD) principles and implements a clean separation of concerns through hexagonal architecture (ports and adapters pattern). - -## Directory Structure - -``` -/src/ -โ”œโ”€โ”€ Domain/ # Core business logic - no framework dependencies -โ”‚ โ”œโ”€โ”€ Forum/ # Forum bounded context -โ”‚ โ”‚ โ”œโ”€โ”€ Model/ # Aggregates and entities -โ”‚ โ”‚ โ”œโ”€โ”€ ValueObject/ # Value objects (PostId, ThreadTitle, etc.) -โ”‚ โ”‚ โ”œโ”€โ”€ Repository/ # Repository interfaces -โ”‚ โ”‚ โ””โ”€โ”€ Exception/ # Domain-specific exceptions -โ”‚ โ”œโ”€โ”€ Tracker/ # BitTorrent tracker bounded context -โ”‚ โ”‚ โ”œโ”€โ”€ Model/ # Torrent, Peer aggregates -โ”‚ โ”‚ โ”œโ”€โ”€ ValueObject/ # InfoHash, PeerId, etc. -โ”‚ โ”‚ โ”œโ”€โ”€ Repository/ # Repository interfaces -โ”‚ โ”‚ โ””โ”€โ”€ Exception/ # Tracker-specific exceptions -โ”‚ โ”œโ”€โ”€ User/ # User management bounded context -โ”‚ โ”‚ โ”œโ”€โ”€ Model/ # User aggregate -โ”‚ โ”‚ โ”œโ”€โ”€ ValueObject/ # UserId, Email, Username -โ”‚ โ”‚ โ”œโ”€โ”€ Repository/ # User repository interface -โ”‚ โ”‚ โ””โ”€โ”€ Exception/ # Authentication/authorization exceptions -โ”‚ โ””โ”€โ”€ Shared/ # Shared kernel - minimal shared concepts -โ”‚ โ”œโ”€โ”€ Model/ # Base classes (AggregateRoot, Entity) -โ”‚ โ”œโ”€โ”€ ValueObject/ # Common value objects (Id, DateTime) -โ”‚ โ””โ”€โ”€ Event/ # Domain events base classes -โ”‚ -โ”œโ”€โ”€ Application/ # Application services - orchestration layer -โ”‚ โ”œโ”€โ”€ Forum/ -โ”‚ โ”‚ โ”œโ”€โ”€ Command/ # Commands (CreatePost, LockThread) -โ”‚ โ”‚ โ”œโ”€โ”€ Query/ # Queries (GetThreadList, SearchPosts) -โ”‚ โ”‚ โ””โ”€โ”€ Handler/ # Command and query handlers -โ”‚ โ”œโ”€โ”€ Tracker/ -โ”‚ โ”‚ โ”œโ”€โ”€ Command/ # Commands (RegisterTorrent, ProcessAnnounce) -โ”‚ โ”‚ โ”œโ”€โ”€ Query/ # Queries (GetPeerList, GetTorrentStats) -โ”‚ โ”‚ โ””โ”€โ”€ Handler/ # Command and query handlers -โ”‚ โ””โ”€โ”€ User/ -โ”‚ โ”œโ”€โ”€ Command/ # Commands (RegisterUser, ChangePassword) -โ”‚ โ”œโ”€โ”€ Query/ # Queries (GetUserProfile, SearchUsers) -โ”‚ โ””โ”€โ”€ Handler/ # Command and query handlers -โ”‚ -โ”œโ”€โ”€ Infrastructure/ # External concerns and implementations -โ”‚ โ”œโ”€โ”€ Persistence/ # Data persistence layer -โ”‚ โ”‚ โ”œโ”€โ”€ Database/ # Database adapter and connection management -โ”‚ โ”‚ โ”œโ”€โ”€ Migration/ # Database migrations -โ”‚ โ”‚ โ””โ”€โ”€ Repository/ # Repository implementations -โ”‚ โ”œโ”€โ”€ Cache/ # Caching implementations -โ”‚ โ”‚ โ”œโ”€โ”€ Redis/ # Redis adapter -โ”‚ โ”‚ โ”œโ”€โ”€ Memcached/ # Memcached adapter -โ”‚ โ”‚ โ””โ”€โ”€ File/ # File-based cache adapter -โ”‚ โ”œโ”€โ”€ Email/ # Email service implementations -โ”‚ โ”‚ โ”œโ”€โ”€ Template/ # Email templates -โ”‚ โ”‚ โ””โ”€โ”€ Transport/ # SMTP, API transports -โ”‚ โ””โ”€โ”€ FileStorage/ # File storage abstractions -โ”‚ โ”œโ”€โ”€ Local/ # Local filesystem storage -โ”‚ โ””โ”€โ”€ S3/ # AWS S3 storage adapter -โ”‚ -โ””โ”€โ”€ Presentation/ # User interface layer - โ”œโ”€โ”€ Http/ # Web interface - โ”‚ โ”œโ”€โ”€ Controllers/ # HTTP controllers - โ”‚ โ”‚ โ”œโ”€โ”€ Admin/ # Admin panel controllers - โ”‚ โ”‚ โ”œโ”€โ”€ Api/ # REST API controllers - โ”‚ โ”‚ โ””โ”€โ”€ Web/ # Web UI controllers - โ”‚ โ”œโ”€โ”€ Middleware/ # HTTP middleware (auth, CORS, etc.) - โ”‚ โ”œโ”€โ”€ Requests/ # Request DTOs and validation - โ”‚ โ””โ”€โ”€ Responses/ # Response transformers - โ””โ”€โ”€ Cli/ # Command line interface - โ””โ”€โ”€ Commands/ # Console commands - -# Additional directories (outside /src/) -/config/ # Application configuration -โ”œโ”€โ”€ app.php # Main application settings -โ”œโ”€โ”€ database.php # Database connections -โ”œโ”€โ”€ cache.php # Cache drivers configuration -โ”œโ”€โ”€ tracker.php # BitTorrent tracker settings -โ””โ”€โ”€ environments/ # Environment-specific overrides - -/tests/ # Test suites (Pest) -โ”œโ”€โ”€ Unit/ # Unit tests (mirrors src/ structure) -โ”œโ”€โ”€ Feature/ # Feature/Integration tests -โ”œโ”€โ”€ Pest.php # Pest configuration -โ””โ”€โ”€ TestCase.php # Base test case -``` - -## Directory README.md Templates - -### Domain Layer READMEs - -#### `/src/Domain/README.md` -```markdown -# Domain Layer - -This directory contains the core business logic of TorrentPier. Code here should: -- Have no dependencies on frameworks or infrastructure -- Represent pure business rules and domain models -- Be testable in isolation -- Use only PHP language features and domain concepts - -## Bounded Contexts -- **Forum**: Discussion forums, posts, threads -- **Tracker**: BitTorrent tracking, peers, torrents -- **User**: User management, authentication, profiles -- **Shared**: Minimal shared concepts between contexts -``` - -#### `/src/Domain/Tracker/Model/README.md` -```markdown -# Tracker Domain Models - -Contains aggregate roots and entities for the BitTorrent tracker: -- `Torrent`: Aggregate root for torrent management -- `Peer`: Entity representing a BitTorrent peer -- `TorrentStatistics`: Value object for torrent stats - -Example: -```php -class Torrent extends AggregateRoot -{ - public function announce(Peer $peer, AnnounceEvent $event): void - { - // Business logic for handling announces - } -} -``` - -#### `/src/Domain/Tracker/ValueObject/README.md` -```markdown -# Tracker Value Objects - -Immutable objects representing domain concepts: -- `InfoHash`: 20-byte torrent identifier -- `PeerId`: Peer client identifier -- `Port`: Network port (1-65535) -- `BytesTransferred`: Upload/download bytes - -Example: -```php -final class InfoHash -{ - private string $hash; - - public function __construct(string $hash) - { - $this->guardAgainstInvalidHash($hash); - $this->hash = $hash; - } -} -``` - -### Application Layer READMEs - -#### `/src/Application/README.md` -```markdown -# Application Layer - -Contains application services that orchestrate domain objects to fulfill use cases. -- Commands: Write operations that change state -- Queries: Read operations for data retrieval -- Handlers: Process commands and queries - -This layer should: -- Coordinate domain objects -- Handle transactions -- Dispatch domain events -- Not contain business logic -``` - -#### `/src/Application/Tracker/Command/README.md` -```markdown -# Tracker Commands - -Commands representing write operations: -- `RegisterTorrentCommand`: Register new torrent -- `UpdateTorrentCommand`: Modify torrent details -- `DeleteTorrentCommand`: Remove torrent from tracker - -Example: -```php -final class RegisterTorrentCommand -{ - public function __construct( - public readonly string $infoHash, - public readonly int $uploaderId, - public readonly string $name, - public readonly int $size - ) {} -} -``` - -### Infrastructure Layer READMEs - -#### `/src/Infrastructure/README.md` -```markdown -# Infrastructure Layer - -Technical implementations and external service adapters: -- Database persistence -- Caching mechanisms -- Email services -- File storage -- Third-party integrations - -Infrastructure depends on domain, not vice versa. -``` - -#### `/src/Infrastructure/Persistence/Repository/README.md` -```markdown -# Repository Implementations - -Concrete implementations of domain repository interfaces: -- Uses database adapter for persistence -- Implements caching strategies -- Handles query optimization -- Supports multiple database backends - -Example: -```php -class TorrentRepository implements TorrentRepositoryInterface -{ - public function __construct( - private DatabaseAdapterInterface $db - ) {} - - public function findByInfoHash(InfoHash $infoHash): ?Torrent - { - // Database adapter implementation - $row = $this->db->select('torrents') - ->where('info_hash', $infoHash->toString()) - ->first(); - - return $row ? $this->hydrateFromRow($row) : null; - } -} -``` - -### Presentation Layer READMEs - -#### `/src/Presentation/README.md` -```markdown -# Presentation Layer - -User interface implementations: -- HTTP controllers for web and API -- CLI commands for console operations -- Request/response handling -- Input validation -- Output formatting - -This layer translates between external format and application format. -``` - -#### `/src/Presentation/Http/Controllers/Api/README.md` -```markdown -# API Controllers - -RESTful API endpoints following OpenAPI specification: -- JSON request/response format -- Proper HTTP status codes -- HATEOAS where applicable -- Rate limiting aware - -Example: -```php -class UserController -{ - public function register(RegisterRequest $request): JsonResponse - { - $command = new RegisterUserCommand( - $request->getUsername(), - $request->getEmail(), - $request->getPassword() - ); - - $userId = $this->commandBus->handle($command); - - return new JsonResponse([ - 'id' => $userId, - 'username' => $request->getUsername() - ], Response::HTTP_CREATED); - } -} -``` - -#### `/src/Presentation/Http/Controllers/Admin/README.md` -```markdown -# Admin Panel Controllers - -Administrative interface controllers with enhanced security: -- Role-based access control (RBAC) -- Audit logging for all actions -- Additional authentication checks -- Administrative dashboards and reports - -Example: -```php -class AdminUserController -{ - public function index(Request $request): Response - { - $query = new GetUsersQuery( - page: $request->getPage(), - filters: $request->getFilters() - ); - - $users = $this->queryBus->handle($query); - - return $this->render('admin/users/index', [ - 'users' => $users, - 'filters' => $request->getFilters() - ]); - } -} -``` - -#### `/config/README.md` -```markdown -# Application Configuration - -System configuration files using PHP arrays for type safety and IDE support: - -- **app.php**: Core application settings - - Site name, URL, timezone - - Debug mode, environment - - Feature flags and toggles - -- **database.php**: Database connection settings - - Multiple connection definitions - - Read/write splitting configuration - - Connection pooling settings - -- **cache.php**: Cache driver configurations - - Redis, Memcached, file-based settings - - TTL defaults per cache type - - Cache key prefixes - -- **tracker.php**: BitTorrent tracker settings - - Announce intervals - - Peer limits - - Ratio requirements - -- **environments/**: Environment-specific overrides - - Development, staging, production settings - - Local developer configurations - -Example database configuration: -```php - env('DB_CONNECTION', 'mysql'), - - 'connections' => [ - 'mysql' => [ - 'driver' => 'mysql', - 'host' => env('DB_HOST', '127.0.0.1'), - 'port' => env('DB_PORT', 3306), - 'database' => env('DB_DATABASE', 'tp'), - 'username' => env('DB_USERNAME', 'root'), - 'password' => env('DB_PASSWORD', ''), - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'options' => [ - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - PDO::ATTR_EMULATE_PREPARES => false, - ], - ], - ], -]; -``` - -## Implementation Order - -1. **Phase 1: Foundation** - - - Create directory structure - - Set up base classes in Domain/Shared - - Configure dependency injection - -2. **Phase 2: Domain Modeling** - - - Implement core aggregates - - Create value objects - - Define repository interfaces - -3. **Phase 3: Application Services** - - - Create commands and queries - - Implement handlers - - Set up event dispatching - -4. **Phase 4: Infrastructure** - - - Implement repositories - - Configure database adapter - - Set up caching - -5. **Phase 5: Presentation** - - - Create controllers - - Implement middleware - - Build CLI commands - -## Migration Strategy - -- Existing code remains in current locations -- New features built in hexagonal architecture -- Gradual migration using strangler fig pattern -- Legacy adapters bridge old and new code -- Feature flags control rollout - -## Key Principles - -1. **Dependency Rule**: Dependencies point inward (Presentation โ†’ Application โ†’ Domain) -2. **Domain Isolation**: Business logic has no framework dependencies -3. **Interface Segregation**: Small, focused interfaces -4. **CQRS**: Separate read and write models -5. **Event-Driven**: Domain events for cross-context communication - -## Testing Strategy - -- **Domain**: Pure unit tests, no mocks needed -- **Application**: Unit tests with mocked repositories -- **Infrastructure**: Integration tests with real services -- **Presentation**: E2E tests for user journeys - -## Notes for Developers - -- Start reading code from the Domain layer -- Business rules live in aggregates, not services -- Use value objects for type safety -- Prefer composition over inheritance -- Keep bounded contexts loosely coupled diff --git a/install/Caddyfile b/install/Caddyfile deleted file mode 100644 index 683d69994..000000000 --- a/install/Caddyfile +++ /dev/null @@ -1,27 +0,0 @@ -# Example Caddy configuration for TorrentPier - -example.com { - root * /path/to/root - encode gzip zstd - php_fastcgi unix//run/php/php-fpm.sock - try_files {path} {path}/ /index.php?{query} - file_server - - @blocked { - path /install/* /internal_data/* /library/* - path /.ht* /.en* - path /.git/* - path *.sql *.tpl *.db *.inc *.log *.md - } - respond @blocked 404 - - redir /sitemap.xml /sitemap/sitemap.xml - - @html_css_js { - path *.html *.css *.js *.json *.xml *.txt - } - header @html_css_js Content-Type "{mime}; charset=utf-8" -} - -# Refer to the Caddy docs for more information: -# https://caddyserver.com/docs/caddyfile diff --git a/install/nginx.conf b/install/nginx.conf deleted file mode 100644 index 49a407ba4..000000000 --- a/install/nginx.conf +++ /dev/null @@ -1,39 +0,0 @@ -# Example nginx configuration for TorrentPier - -server { - listen 80; # port - server_name example.com; # your domain - root /path/to/root; # folder with TorrentPier installed - index index.php; - charset utf-8; - - location / { - try_files \$uri \$uri/ /index.php?\$args; - } - - location ~ \/(install|internal_data|library)\/ { - return 404; - } - - location ~ /\.(ht|en) { - return 404; - } - - location ~ /\.git { - return 404; - } - - location ~ \.(.*sql|tpl|db|inc|log|md)$ { - return 404; - } - - rewrite ^/sitemap.xml$ /sitemap/sitemap.xml; - - location ~ \.php$ { - include fastcgi_params; - fastcgi_pass unix:/run/php/php-fpm.sock; - fastcgi_index index.php; - fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name; - include fastcgi_params; - } -} diff --git a/phpunit.xml b/phpunit.xml deleted file mode 100644 index e6198e0e7..000000000 --- a/phpunit.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - ./tests - - - - - app - src - - - diff --git a/src/Application/Forum/Command/README.md b/src/Application/Forum/Command/README.md deleted file mode 100644 index eae098d4a..000000000 --- a/src/Application/Forum/Command/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Forum Commands - -Commands representing write operations: - -- `CreateThreadCommand`: Start new discussion -- `CreatePostCommand`: Reply to thread -- `EditPostCommand`: Modify existing post -- `LockThreadCommand`: Lock thread from replies -- `DeletePostCommand`: Remove post - -Commands are simple DTOs containing operation data. \ No newline at end of file diff --git a/src/Application/Forum/Handler/README.md b/src/Application/Forum/Handler/README.md deleted file mode 100644 index 4af804611..000000000 --- a/src/Application/Forum/Handler/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Forum Handlers - -Command and query handlers for forum operations: - -- `CreateThreadHandler`: Handles thread creation -- `CreatePostHandler`: Handles post creation -- `GetThreadListHandler`: Retrieves thread listings - -Handlers coordinate between domain and infrastructure layers. \ No newline at end of file diff --git a/src/Application/Forum/Query/README.md b/src/Application/Forum/Query/README.md deleted file mode 100644 index 845ef3948..000000000 --- a/src/Application/Forum/Query/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Forum Queries - -Read-only queries for forum data: - -- `GetThreadListQuery`: Paginated thread listing -- `GetPostsByThreadQuery`: Thread posts with pagination -- `SearchPostsQuery`: Full-text post search -- `GetForumStatsQuery`: Forum statistics - -Queries return DTOs optimized for presentation. \ No newline at end of file diff --git a/src/Application/README.md b/src/Application/README.md deleted file mode 100644 index f7a27973c..000000000 --- a/src/Application/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Application Layer - -Contains application services that orchestrate domain objects to fulfill use cases. - -- Commands: Write operations that change state -- Queries: Read operations for data retrieval -- Handlers: Process commands and queries - -This layer should: - -- Coordinate domain objects -- Handle transactions -- Dispatch domain events -- Not contain business logic \ No newline at end of file diff --git a/src/Application/Tracker/Command/README.md b/src/Application/Tracker/Command/README.md deleted file mode 100644 index 800c6579d..000000000 --- a/src/Application/Tracker/Command/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Tracker Commands - -Commands representing write operations: - -- `RegisterTorrentCommand`: Register new torrent -- `UpdateTorrentCommand`: Modify torrent details -- `DeleteTorrentCommand`: Remove torrent from tracker - -Example: - -```php -final class RegisterTorrentCommand -{ - public function __construct( - public readonly string $infoHash, - public readonly int $uploaderId, - public readonly string $name, - public readonly int $size - ) {} -} -``` \ No newline at end of file diff --git a/src/Application/Tracker/Handler/README.md b/src/Application/Tracker/Handler/README.md deleted file mode 100644 index b30755d33..000000000 --- a/src/Application/Tracker/Handler/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Tracker Handlers - -Command and query handlers for tracker operations: - -- `RegisterTorrentHandler`: Processes torrent registration -- `GetTorrentDetailsHandler`: Retrieves torrent information -- `UpdateTorrentHandler`: Handles torrent updates - -Note: High-performance announce operations bypass this layer. \ No newline at end of file diff --git a/src/Application/Tracker/Query/README.md b/src/Application/Tracker/Query/README.md deleted file mode 100644 index 05e42e39f..000000000 --- a/src/Application/Tracker/Query/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Tracker Queries - -Read-only queries for tracker data: - -- `GetTorrentDetailsQuery`: Single torrent information -- `GetPeerListQuery`: Active peers for torrent -- `GetTorrentStatsQuery`: Download/upload statistics -- `SearchTorrentsQuery`: Browse and filter torrents - -Optimized for high-performance read operations. \ No newline at end of file diff --git a/src/Application/User/Command/README.md b/src/Application/User/Command/README.md deleted file mode 100644 index fbf0342be..000000000 --- a/src/Application/User/Command/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# User Commands - -Commands for user management operations: - -- `RegisterUserCommand`: New user registration -- `ChangePasswordCommand`: Password update -- `UpdateProfileCommand`: Profile modifications -- `ActivateUserCommand`: Account activation -- `BanUserCommand`: User suspension - -These commands trigger domain events for cross-context synchronization. \ No newline at end of file diff --git a/src/Application/User/Handler/README.md b/src/Application/User/Handler/README.md deleted file mode 100644 index 8f34ce418..000000000 --- a/src/Application/User/Handler/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# User Handlers - -Command and query handlers for user operations: - -- `RegisterUserHandler`: Processes user registration -- `ChangePasswordHandler`: Handles password changes -- `GetUserProfileHandler`: Retrieves user profiles - -Handlers manage transactions and event dispatching. \ No newline at end of file diff --git a/src/Application/User/Query/README.md b/src/Application/User/Query/README.md deleted file mode 100644 index 1307a51ec..000000000 --- a/src/Application/User/Query/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# User Queries - -Read operations for user data: - -- `GetUserProfileQuery`: User profile details -- `SearchUsersQuery`: User search with filters -- `GetUserStatisticsQuery`: Upload/download stats -- `GetUserPermissionsQuery`: Authorization data - -Returns DTOs suitable for presentation layer. \ No newline at end of file diff --git a/src/Cache/README.md b/src/Cache/README.md deleted file mode 100644 index e2151819b..000000000 --- a/src/Cache/README.md +++ /dev/null @@ -1,422 +0,0 @@ -# Unified Cache System - -A modern, unified caching solution for TorrentPier 3.0 that uses **Nette Caching** internally. **Breaking changes**: This replaces the legacy Cache and Datastore APIs and requires code migration. - -## Overview - -The Unified Cache System addresses the complexity and duplication in TorrentPier's caching architecture by: - -- **Unifying** Cache and Datastore systems into a single, coherent solution -- **Modernizing** the caching layer with Nette's advanced features and breaking changes for better architecture -- **Reducing** complexity and maintenance overhead -- **Improving** performance with efficient singleton pattern and advanced features - -## Architecture - -### Core Components - -1. **UnifiedCacheSystem** - Main singleton orchestrator following TorrentPier's architectural patterns -2. **CacheManager** - Cache interface using Nette Caching internally with singleton pattern -3. **DatastoreManager** - Datastore interface that uses CacheManager internally for unified functionality - -### Singleton Architecture - -The system follows TorrentPier's consistent singleton pattern, similar to `config()`, `dev()`, `censor()`, and `DB()`: - -```php -// Main singleton instance -TorrentPier\Cache\UnifiedCacheSystem::getInstance(config()->all()); - -// Clean global functions with proper return types -function CACHE(string $cache_name): \TorrentPier\Cache\CacheManager -function datastore(): \TorrentPier\Cache\DatastoreManager - -// Usage (exactly like before) -$cache = CACHE('bb_cache'); -$datastore = datastore(); -``` - -### Key Benefits - -- โœ… **Single Source of Truth**: One caching system instead of two separate ones -- โœ… **Modern Foundation**: Built on Nette Caching v3.3 with all its advanced features -- โœ… **Zero Breaking Changes**: All existing `CACHE()` and `$datastore` calls work unchanged -- โœ… **Consistent Architecture**: Proper singleton pattern matching other TorrentPier services -- โœ… **Advanced Features**: Dependencies, tags, bulk operations, memoization, output buffering -- โœ… **Better Debugging**: Unified debug interface with compatibility for Dev.php -- โœ… **Performance**: 456,647+ operations per second with efficient memory usage -- โœ… **Clean Architecture**: No redundant configuration logic, single storage creation path - -## Usage - -### Basic Cache Operations (100% Backward Compatible) - -```php -// All existing cache calls work exactly the same -$cache = CACHE('bb_cache'); -$value = $cache->get('key'); -$cache->set('key', $value, 3600); -$cache->rm('key'); -``` - -### Datastore Operations (100% Backward Compatible) - -```php -// All existing datastore calls work exactly the same -$datastore = datastore(); -$forums = $datastore->get('cat_forums'); -$datastore->store('custom_data', $data); -$datastore->update(['cat_forums', 'stats']); -``` - -### Advanced Nette Caching Features - -```php -// Get cache manager for advanced features -$cache = CACHE('bb_cache'); - -// Load with callback (compute if not cached) -$value = $cache->load('expensive_key', function() { - return expensive_computation(); -}); - -// Cache with time expiration -$cache->save('key', $value, [ - \Nette\Caching\Cache::Expire => '1 hour' -]); - -// Cache with file dependencies -$cache->save('config', $data, [ - \Nette\Caching\Cache::Files => ['/path/to/config.php'] -]); - -// Memoize function calls -$result = $cache->call('expensive_function', $param1, $param2); - -// Bulk operations -$values = $cache->bulkLoad(['key1', 'key2', 'key3'], function($key) { - return "computed_value_for_$key"; -}); - -// Clean by tags (requires SQLite storage) -$cache->clean([\Nette\Caching\Cache::Tags => ['user-123']]); - -// Output buffering -$content = $cache->capture('output_key', function() { - echo "This content will be cached"; -}); -``` - -### Datastore Advanced Features - -```php -$datastore = datastore(); - -// All standard operations work -$forums = $datastore->get('cat_forums'); -$datastore->store('custom_data', $data); - -// Access underlying CacheManager for advanced features -$manager = $datastore->getCacheManager(); -$value = $manager->load('complex_data', function() { - return build_complex_data(); -}, [ - \Nette\Caching\Cache::Expire => '30 minutes', - \Nette\Caching\Cache::Tags => ['forums', 'categories'] -]); -``` - -## Integration & Initialization - -### Automatic Integration - -The system integrates seamlessly in `library/includes/functions.php`: - -```php -// Singleton initialization (done once) -TorrentPier\Cache\UnifiedCacheSystem::getInstance(config()->all()); - -// Global functions provide backward compatibility -function CACHE(string $cache_name): \TorrentPier\Cache\CacheManager { - return TorrentPier\Cache\UnifiedCacheSystem::getInstance()->getCache($cache_name); -} - -function datastore(): \TorrentPier\Cache\DatastoreManager { - return TorrentPier\Cache\UnifiedCacheSystem::getInstance()->getDatastore(config()->get('datastore_type', 'file')); -} -``` - -### Debug Compatibility - -The system maintains full compatibility with Dev.php debugging: - -```php -// Dev.php can access debug information via magic __get() methods -$cache = CACHE('bb_cache'); -$debug_info = $cache->dbg; // Array of operations -$engine_name = $cache->engine; // Storage engine name -$total_time = $cache->sql_timetotal; // Total operation time - -$datastore = datastore(); -$datastore_debug = $datastore->dbg; // Datastore debug info -``` - -## Configuration - -The system uses existing configuration seamlessly: - -```php -// library/config.php -$bb_cfg['cache'] = [ - 'db_dir' => realpath(BB_ROOT) . '/internal_data/cache/filecache/', - 'prefix' => 'tp_', - 'engines' => [ - 'bb_cache' => ['file'], // Uses Nette FileStorage - 'session_cache' => ['sqlite'], // Uses Nette SQLiteStorage - 'tr_cache' => ['file'], // Uses Nette FileStorage - // ... other caches - ], -]; - -$bb_cfg['datastore_type'] = 'file'; // Uses Nette FileStorage -``` - -## Storage Types - -### Supported Storage Types - -| Legacy Type | Nette Storage | Features | -|------------|---------------|----------| -| `file` | `FileStorage` | File-based, persistent, dependencies | -| `sqlite` | `SQLiteStorage` | Database, supports tags and complex dependencies | -| `memory` | `MemoryStorage` | In-memory, fastest, non-persistent | -| `memcached` | `MemcachedStorage` | Distributed memory, high-performance | - -### Storage Features Comparison - -| Feature | FileStorage | SQLiteStorage | MemoryStorage | MemcachedStorage | -|---------|-------------|---------------|---------------|------------------| -| Persistence | โœ… | โœ… | โŒ | โœ… | -| File Dependencies | โœ… | โœ… | โœ… | โœ… | -| Tags | โŒ | โœ… | โœ… | โŒ | -| Callbacks | โœ… | โœ… | โœ… | โœ… | -| Bulk Operations | โœ… | โœ… | โœ… | โœ… | -| Performance | High | Medium | Highest | Very High | -| Distributed | โŒ | โŒ | โŒ | โœ… | - -## Migration Guide - -### Zero Migration Required - -All existing code continues to work without any modifications: - -```php -// โœ… This works exactly as before - no changes needed -$cache = CACHE('bb_cache'); -$forums = $datastore->get('cat_forums'); - -// โœ… All debug functionality preserved -global $CACHES; -foreach ($CACHES->obj as $cache_name => $cache_obj) { - echo "Cache: $cache_name\n"; -} -``` - -### Enhanced Capabilities for New Code - -New code can take advantage of advanced features: - -```php -// โœ… Enhanced caching with dependencies and tags -$cache = CACHE('bb_cache'); -$forums = $cache->load('forums_with_stats', function() { - return build_forums_with_statistics(); -}, [ - \Nette\Caching\Cache::Expire => '1 hour', - \Nette\Caching\Cache::Files => ['/path/to/forums.config'], - \Nette\Caching\Cache::Tags => ['forums', 'statistics'] -]); - -// โœ… Function memoization -$expensive_result = $cache->call('calculate_user_stats', $user_id); - -// โœ… Output buffering -$rendered_page = $cache->capture("page_$page_id", function() { - include_template('complex_page.php'); -}); -``` - -## Performance Benefits - -### Benchmarks - -- **456,647+ operations per second** in production testing -- **Singleton efficiency**: Each cache namespace instantiated only once -- **Memory optimization**: Shared storage and efficient instance management -- **Nette optimizations**: Advanced algorithms for cache invalidation and cleanup - -### Advanced Features Performance - -- **Bulk Operations**: Load multiple keys in single operation -- **Memoization**: Automatic function result caching with parameter-based keys -- **Dependencies**: Smart cache invalidation based on files, time, or custom logic -- **Output Buffering**: Cache generated output directly without intermediate storage - -## Critical Issues Resolved - -### Sessions Compatibility - -**Issue**: Legacy cache returns `false` for missing values, Nette returns `null` -**Solution**: CacheManager->get() returns `$result ?? false` for backward compatibility - -### Debug Integration - -**Issue**: Dev.php expected `->db` property on cache objects for debug logging -**Solution**: Added `__get()` magic methods returning compatible debug objects with `dbg[]`, `engine`, `sql_timetotal` properties - -### Architecture Consistency - -**Issue**: Inconsistent initialization pattern compared to other TorrentPier singletons -**Solution**: Converted to proper singleton pattern with `getInstance()` method and clean global functions - -## Implementation Details - -### Architecture Flow (Refactored) - -**Clean, Non-Redundant Architecture:** -``` -UnifiedCacheSystem (singleton) -โ”œโ”€โ”€ _buildStorage() โ†’ Creates Nette Storage instances directly -โ”œโ”€โ”€ get_cache_obj() โ†’ Returns CacheManager with pre-built storage -โ””โ”€โ”€ getDatastore() โ†’ Returns DatastoreManager with pre-built storage - -CacheManager (receives pre-built Storage) -โ”œโ”€โ”€ Constructor receives: Storage instance + minimal config -โ”œโ”€โ”€ No redundant initializeStorage() switch statement -โ””โ”€โ”€ Focuses purely on cache operations - -DatastoreManager (uses CacheManager internally) -โ”œโ”€โ”€ Constructor receives: Storage instance + minimal config -โ”œโ”€โ”€ Uses CacheManager internally for unified functionality -โ””โ”€โ”€ Maintains datastore-specific methods and compatibility -``` - -**Benefits of Refactored Architecture:** -- **Single Source of Truth**: Only UnifiedCacheSystem creates storage instances -- **No Redundancy**: Eliminated duplicate switch statements and configuration parsing -- **Cleaner Separation**: CacheManager focuses on caching, not storage creation -- **Impossible Path Bugs**: Storage is pre-built, no configuration mismatches possible -- **Better Maintainability**: One place to modify storage creation logic - -### Directory Structure - -``` -src/Cache/ -โ”œโ”€โ”€ CacheManager.php # Cache interface with Nette Caching + singleton pattern -โ”œโ”€โ”€ DatastoreManager.php # Datastore interface using CacheManager internally -โ”œโ”€โ”€ UnifiedCacheSystem.php # Main singleton orchestrator + storage factory -โ””โ”€โ”€ README.md # This documentation -``` - -### Removed Development Files - -The following development and testing files were removed after successful integration: -- `Example.php` - Migration examples (no longer needed) -- `Integration.php` - Testing utilities (production-ready) -- `cache_test.php` - Performance testing script (completed) - -### Key Features Achieved - -1. **100% Backward Compatibility**: All existing APIs work unchanged -2. **Modern Foundation**: Built on stable, well-tested Nette Caching v3.3 -3. **Advanced Features**: Dependencies, tags, bulk operations, memoization, output buffering -4. **Efficient Singletons**: Memory-efficient instance management following TorrentPier patterns -5. **Unified Debugging**: Consistent debug interface compatible with Dev.php -6. **Production Ready**: Comprehensive error handling, validation, and performance optimization -7. **Clean Architecture**: Eliminated redundant configuration logic and switch statements -8. **Single Storage Source**: All storage creation centralized in UnifiedCacheSystem - -### Architectural Consistency - -Following TorrentPier's established patterns: - -```php -// Consistent with other singletons -config() -> Config::getInstance() -dev() -> Dev::getInstance() -censor() -> Censor::getInstance() -DB() -> DB::getInstance() -CACHE() -> UnifiedCacheSystem::getInstance()->getCache() -datastore() -> UnifiedCacheSystem::getInstance()->getDatastore() -``` - -## Testing & Verification - -### Backward Compatibility Verified - -```php -// โœ… All existing functionality preserved -$cache = CACHE('bb_cache'); -assert($cache->set('test', 'value', 60) === true); -assert($cache->get('test') === 'value'); -assert($cache->rm('test') === true); - -$datastore = datastore(); -$datastore->store('test_item', ['data' => 'test']); -assert($datastore->get('test_item')['data'] === 'test'); -``` - -### Advanced Features Verified - -```php -// โœ… Nette features working correctly -$cache = CACHE('advanced_test'); - -// Memoization -$result1 = $cache->call('expensive_function', 'param'); -$result2 = $cache->call('expensive_function', 'param'); // From cache - -// Dependencies -$cache->save('file_dependent', $data, [ - \Nette\Caching\Cache::Files => [__FILE__] -]); - -// Bulk operations -$values = $cache->bulkLoad(['key1', 'key2'], function($key) { - return "value_$key"; -}); - -// Performance: 456,647+ ops/sec verified -``` - -### Debug Functionality Verified - -```php -// โœ… Dev.php integration working -$cache = CACHE('bb_cache'); -$debug = $cache->dbg; // Returns array of operations -$engine = $cache->engine; // Returns storage type -$time = $cache->sql_timetotal; // Returns total time - -// โœ… Singleton behavior verified -$instance1 = TorrentPier\Cache\UnifiedCacheSystem::getInstance(); -$instance2 = TorrentPier\Cache\UnifiedCacheSystem::getInstance(); -assert($instance1 === $instance2); // Same instance -``` - -## Future Enhancements - -### Planned Storage Implementations -- Redis storage adapter for Nette -- Memcached storage adapter for Nette -- APCu storage adapter for Nette - -### Advanced Features Roadmap -- Distributed caching support -- Cache warming and preloading -- Advanced metrics and monitoring -- Multi-tier caching strategies - ---- - -This unified cache system represents a significant architectural improvement in TorrentPier while ensuring seamless backward compatibility and providing a robust foundation for future enhancements. The clean singleton pattern, advanced Nette Caching features, and comprehensive debug support make it a production-ready replacement for the legacy Cache and Datastore systems. diff --git a/src/Database/README.md b/src/Database/README.md deleted file mode 100644 index b7cc5b05b..000000000 --- a/src/Database/README.md +++ /dev/null @@ -1,362 +0,0 @@ -# TorrentPier Database Layer - -This directory contains the new database layer for TorrentPier 3.0 that uses Nette Database internally. **Breaking changes**: This replaces the legacy SqlDb interface and requires code migration. - -## Overview - -The new database system has completely replaced the legacy SqlDb/Dbs system and provides: - -- **Modern API** - New `DB()->method()` calls with improved functionality -- **Nette Database integration** - Modern, efficient database layer under the hood -- **Singleton pattern** - Efficient connection management -- **Complete feature parity** - All original functionality preserved - -## Architecture - -### Classes - -1. **`Database`** - Main singleton database class using Nette Database Connection -2. **`DatabaseFactory`** - Factory that has completely replaced the legacy SqlDb/Dbs system -3. **`DatabaseDebugger`** - Dedicated debug functionality extracted from Database class -4. **`DebugSelection`** - Debug-enabled wrapper for Nette Database Selection - -### Key Features - -- **Singleton Pattern**: Ensures single database connection per server configuration -- **Multiple Database Support**: Handles multiple database servers via DatabaseFactory -- **Raw SQL Support**: Uses Nette Database's Connection class (SQL way) for minimal code impact -- **Complete Error Handling**: Maintains existing error handling behavior -- **Full Debug Support**: Preserves all debugging, logging, and explain functionality -- **Performance Tracking**: Query timing and slow query detection -- **Clean Architecture**: Debug functionality extracted to dedicated DatabaseDebugger class -- **Modular Design**: Single responsibility principle with separate debug and database concerns - -## Implementation Status - -- โœ… **Complete Replacement**: Legacy SqlDb/Dbs classes have been removed from the codebase -- โœ… **Backward Compatibility**: All existing `DB()->method()` calls work unchanged -- โœ… **Debug System**: Full explain(), logging, and performance tracking -- โœ… **Error Handling**: Complete error handling with sql_error() support -- โœ… **Connection Management**: Singleton pattern with proper initialization -- โœ… **Clean Architecture**: Debug functionality extracted to dedicated classes -- โœ… **Class Renaming**: Renamed DB โ†’ Database, DbFactory โ†’ DatabaseFactory for consistency - -## Usage - -### Standard Database Operations -```php -// All existing code works unchanged -$user = DB()->fetch_row("SELECT * FROM users WHERE id = ?", 123); -$users = DB()->fetch_rowset("SELECT * FROM users"); -$affected = DB()->affected_rows(); - -// Raw queries -$result = DB()->sql_query("UPDATE users SET status = ? WHERE id = ?", 1, 123); - -// Data building -$data = ['name' => 'John', 'email' => 'john@example.com']; -$sql = DB()->build_array('INSERT', $data); -``` - -### Multiple Database Servers -```php -// Access different database servers -$main_db = DB('db'); // Main database -$tracker_db = DB('tr'); // Tracker database -$stats_db = DB('stats'); // Statistics database -``` - -### Error Handling -```php -$result = DB()->sql_query("SELECT * FROM users"); -if (!$result) { - $error = DB()->sql_error(); - echo "Error: " . $error['message']; -} -``` - -## Configuration - -The database configuration is handled through the existing TorrentPier config system: - -```php -// Initialized in common.php -TorrentPier\Database\DatabaseFactory::init( - config()->get('db'), // Database configurations - config()->get('db_alias', []) // Database aliases -); -``` - -## Benefits - -### Performance -- **Efficient Connections**: Singleton pattern prevents connection overhead -- **Modern Database Layer**: Nette Database v3.2 optimizations -- **Resource Management**: Automatic cleanup and proper connection handling - -### Maintainability -- **Modern Codebase**: Uses current PHP standards and type declarations -- **Better Architecture**: Clean separation of concerns -- **Nette Ecosystem**: Part of actively maintained Nette framework - -### Reliability -- **Proven Technology**: Nette Database is battle-tested -- **Regular Updates**: Automatic security and bug fixes through composer -- **Type Safety**: Better error detection and IDE support - -## Debugging Features - -All original debugging features are preserved and enhanced: - -### Query Logging -- SQL query logging with timing -- Slow query detection and logging -- Memory usage tracking - -### Debug Information -```php -// Enable debugging (same as before) -DB()->debug('start'); -// ... run queries ... -DB()->debug('stop'); -``` - -### Explain Functionality -```php -// Explain queries (same interface as before) -DB()->explain('start'); -DB()->explain('display'); -``` - -## Technical Details - -### Nette Database Integration -- Uses Nette Database **Connection** class (SQL way) -- Maintains raw SQL approach for minimal migration impact -- PDO-based with proper parameter binding - -### Compatibility Layer -- All original method signatures preserved -- Same return types and behavior -- Error handling matches original implementation - -### Connection Management -- Single connection per database server -- Lazy connection initialization -- Proper connection cleanup - -## Migration Notes - -This is a **complete replacement** that maintains 100% backward compatibility: - -1. **No Code Changes Required**: All existing `DB()->method()` calls work unchanged -2. **Same Configuration**: Uses existing database configuration -3. **Same Behavior**: Error handling, return values, and debugging work identically -4. **Enhanced Performance**: Better connection management and modern database layer - -## Dependencies - -- **Nette Database v3.2**: Already included in composer.json -- **PHP 8.0+**: Required for type declarations and modern features - -## Files - -- `Database.php` - Main database class with full backward compatibility -- `DatabaseFactory.php` - Factory for managing database instances -- `DatabaseDebugger.php` - Dedicated debug functionality class -- `DebugSelection.php` - Debug-enabled Nette Selection wrapper -- `README.md` - This documentation - -## Future Enhancement: Gradual Migration to Nette Explorer - -While the current implementation uses Nette Database's **Connection** class (SQL way) for maximum compatibility, TorrentPier can gradually migrate to **Nette Database Explorer** for more modern ORM-style database operations. - -### Phase 1: Hybrid Approach - -Add Explorer support alongside existing Connection-based methods: - -```php -// Current Connection-based approach (maintains compatibility) -$users = DB()->fetch_rowset("SELECT * FROM users WHERE status = ?", 1); - -// New Explorer-based approach (added gradually) -$users = DB()->table('users')->where('status', 1)->fetchAll(); -``` - -### Phase 2: Explorer Method Examples - -#### Basic Table Operations -```php -// Select operations -$user = DB()->table('users')->get(123); // Get by ID -$users = DB()->table('users')->where('status', 1)->fetchAll(); // Where condition -$count = DB()->table('users')->where('status', 1)->count(); // Count records - -// Insert operations -$user_id = DB()->table('users')->insert([ - 'username' => 'john', - 'email' => 'john@example.com', - 'reg_time' => time() -]); - -// Update operations -DB()->table('users') - ->where('id', 123) - ->update(['last_visit' => time()]); - -// Delete operations -DB()->table('users') - ->where('status', 0) - ->delete(); -``` - -#### Advanced Explorer Features -```php -// Joins and relationships -$posts = DB()->table('posts') - ->select('posts.*, users.username') - ->where('posts.forum_id', 5) - ->order('posts.post_time DESC') - ->limit(20) - ->fetchAll(); - -// Aggregations -$stats = DB()->table('torrents') - ->select('forum_id, COUNT(*) as total, SUM(size) as total_size') - ->where('approved', 1) - ->group('forum_id') - ->fetchAll(); - -// Subqueries -$active_users = DB()->table('users') - ->where('last_visit > ?', time() - 86400) - ->where('id IN', DB()->table('posts') - ->select('user_id') - ->where('post_time > ?', time() - 86400) - ) - ->fetchAll(); -``` - -#### Working with Related Data -```php -// One-to-many relationships -$user = DB()->table('users')->get(123); -$user_posts = $user->related('posts')->order('post_time DESC'); - -// Many-to-many through junction table -$torrent = DB()->table('torrents')->get(456); -$seeders = $torrent->related('bt_tracker', 'torrent_id') - ->where('seeder', 'yes') - ->select('user_id'); -``` - -### Phase 3: Migration Strategy - -#### Step-by-Step Conversion -1. **Identify Patterns**: Find common SQL patterns in the codebase -2. **Create Helpers**: Build wrapper methods for complex queries -3. **Test Incrementally**: Convert one module at a time -4. **Maintain Compatibility**: Keep both approaches during transition - -#### Example Migration Pattern -```php -// Before: Raw SQL -$result = DB()->sql_query(" - SELECT t.*, u.username - FROM torrents t - JOIN users u ON t.poster_id = u.user_id - WHERE t.forum_id = ? AND t.approved = 1 - ORDER BY t.reg_time DESC - LIMIT ? -", $forum_id, $limit); - -$torrents = []; -while ($row = DB()->sql_fetchrow($result)) { - $torrents[] = $row; -} - -// After: Explorer ORM -$torrents = DB()->table('torrents') - ->alias('t') - ->select('t.*, u.username') - ->where('t.forum_id', $forum_id) - ->where('t.approved', 1) - ->order('t.reg_time DESC') - ->limit($limit) - ->fetchAll(); -``` - -### Phase 4: Advanced Explorer Features - -#### Custom Repository Classes -```php -// Create specialized repository classes -class TorrentRepository -{ - private $db; - - public function __construct($db) - { - $this->db = $db; - } - - public function getApprovedByForum($forum_id, $limit = 20) - { - return $this->db->table('torrents') - ->where('forum_id', $forum_id) - ->where('approved', 1) - ->order('reg_time DESC') - ->limit($limit) - ->fetchAll(); - } - - public function getTopSeeded($limit = 10) - { - return $this->db->table('torrents') - ->where('approved', 1) - ->order('seeders DESC') - ->limit($limit) - ->fetchAll(); - } -} - -// Usage -$torrentRepo = new TorrentRepository(DB()); -$popular = $torrentRepo->getTopSeeded(); -``` - -#### Database Events and Caching -```php -// Add caching layer -$cached_result = DB()->table('users') - ->where('status', 1) - ->cache('active_users', 3600) // Cache for 1 hour - ->fetchAll(); - -// Database events for logging -DB()->onQuery[] = function ($query, $parameters, $time) { - if ($time > 1.0) { // Log slow queries - error_log("Slow query ({$time}s): $query"); - } -}; -``` - -### Benefits of Explorer Migration - -#### Developer Experience -- **Fluent Interface**: Chainable method calls -- **IDE Support**: Better autocomplete and type hints -- **Less SQL**: Reduced raw SQL writing -- **Built-in Security**: Automatic parameter binding - -#### Code Quality -- **Readable Code**: Self-documenting query building -- **Reusable Patterns**: Common queries become methods -- **Type Safety**: Better error detection -- **Testing**: Easier to mock and test - -#### Performance -- **Query Optimization**: Explorer can optimize queries -- **Lazy Loading**: Load related data only when needed -- **Connection Pooling**: Better resource management -- **Caching Integration**: Built-in caching support diff --git a/src/Domain/Forum/Exception/README.md b/src/Domain/Forum/Exception/README.md deleted file mode 100644 index 07a483fe6..000000000 --- a/src/Domain/Forum/Exception/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Forum Domain Exceptions - -Domain-specific exceptions for forum operations: - -- `ThreadLockedException`: Attempt to post in locked thread -- `PostEditTimeExpiredException`: Edit time limit exceeded -- `ForumAccessDeniedException`: Insufficient permissions -- `InvalidPostContentException`: Post validation failed \ No newline at end of file diff --git a/src/Domain/Forum/Model/README.md b/src/Domain/Forum/Model/README.md deleted file mode 100644 index 6dafb0894..000000000 --- a/src/Domain/Forum/Model/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Forum Domain Models - -Contains aggregate roots and entities for the forum system: - -- `Forum`: Forum category aggregate -- `Thread`: Discussion thread aggregate root -- `Post`: Individual post entity -- `Attachment`: File attachment entity - -Business rules enforced at this level: - -- Post editing time limits -- Thread locking rules -- Forum access permissions -- Post moderation workflow \ No newline at end of file diff --git a/src/Domain/Forum/Repository/README.md b/src/Domain/Forum/Repository/README.md deleted file mode 100644 index 1a8430801..000000000 --- a/src/Domain/Forum/Repository/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Forum Repository Interfaces - -Repository interfaces for forum aggregates: - -- `ForumRepositoryInterface`: Forum aggregate persistence -- `ThreadRepositoryInterface`: Thread aggregate persistence -- `PostRepositoryInterface`: Post queries (read-only) - -These interfaces define contracts that infrastructure must implement. \ No newline at end of file diff --git a/src/Domain/Forum/ValueObject/README.md b/src/Domain/Forum/ValueObject/README.md deleted file mode 100644 index eb8aec659..000000000 --- a/src/Domain/Forum/ValueObject/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Forum Value Objects - -Immutable objects representing forum concepts: - -- `PostId`: Unique post identifier -- `ThreadTitle`: Thread title with validation -- `PostContent`: Formatted post content -- `ForumSlug`: URL-friendly forum identifier - -These objects ensure type safety and encapsulate validation rules. \ No newline at end of file diff --git a/src/Domain/README.md b/src/Domain/README.md deleted file mode 100644 index cb9254a68..000000000 --- a/src/Domain/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Domain Layer - -This directory contains the core business logic of TorrentPier. Code here should: - -- Have no dependencies on frameworks or infrastructure -- Represent pure business rules and domain models -- Be testable in isolation -- Use only PHP language features and domain concepts - -## Bounded Contexts - -- **Forum**: Discussion forums, posts, threads -- **Tracker**: BitTorrent tracking, peers, torrents -- **User**: User management, authentication, profiles -- **Shared**: Minimal shared concepts between contexts \ No newline at end of file diff --git a/src/Domain/Shared/Event/README.md b/src/Domain/Shared/Event/README.md deleted file mode 100644 index 11656fb4a..000000000 --- a/src/Domain/Shared/Event/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Domain Events - -Base classes and interfaces for domain event system: - -- `DomainEvent`: Base event interface -- `EventRecording`: Trait for aggregate event recording -- `AggregateHistory`: Event sourcing support - -Example events: - -- `UserRegisteredEvent` -- `TorrentUploadedEvent` -- `ThreadCreatedEvent` - -Events enable loose coupling between bounded contexts. \ No newline at end of file diff --git a/src/Domain/Shared/Exception/README.md b/src/Domain/Shared/Exception/README.md deleted file mode 100644 index 2c5a556bd..000000000 --- a/src/Domain/Shared/Exception/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Shared Domain Exceptions - -Base exception classes used across all bounded contexts: - -- `DomainException`: Base domain exception -- `InvalidArgumentException`: Invalid input validation -- `EntityNotFoundException`: Generic entity not found -- `BusinessRuleViolationException`: Business rule violations - -These provide common exception handling patterns without coupling contexts. \ No newline at end of file diff --git a/src/Domain/Shared/Model/README.md b/src/Domain/Shared/Model/README.md deleted file mode 100644 index 4daf36150..000000000 --- a/src/Domain/Shared/Model/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Shared Domain Models - -Base classes and interfaces used across bounded contexts: - -- `AggregateRoot`: Base class for all aggregates -- `Entity`: Base class for entities -- `DomainEvent`: Interface for domain events -- `ValueObject`: Base value object interface - -These provide common functionality while maintaining context boundaries. \ No newline at end of file diff --git a/src/Domain/Shared/Repository/README.md b/src/Domain/Shared/Repository/README.md deleted file mode 100644 index 999f9d300..000000000 --- a/src/Domain/Shared/Repository/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Shared Repository Interfaces - -Common repository patterns and base interfaces: - -- `RepositoryInterface`: Base repository contract -- `ReadOnlyRepositoryInterface`: Query-only operations -- `AggregateRepositoryInterface`: Aggregate-specific methods - -These define common persistence patterns without implementation details. \ No newline at end of file diff --git a/src/Domain/Shared/ValueObject/README.md b/src/Domain/Shared/ValueObject/README.md deleted file mode 100644 index afc5b7bc6..000000000 --- a/src/Domain/Shared/ValueObject/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Shared Value Objects - -Common value objects used across contexts: - -- `Id`: Generic identifier base class -- `DateTime`: Immutable datetime wrapper -- `Money`: Monetary value representation -- `Percentage`: Percentage value (0-100) - -These are minimal shared concepts that don't violate bounded context principles. \ No newline at end of file diff --git a/src/Domain/Tracker/Exception/README.md b/src/Domain/Tracker/Exception/README.md deleted file mode 100644 index 73ae0f882..000000000 --- a/src/Domain/Tracker/Exception/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Tracker Domain Exceptions - -Domain-specific exceptions for tracker operations: - -- `InvalidInfoHashException`: Malformed info hash -- `InactiveTorrentException`: Torrent not active -- `PeerLimitExceededException`: Too many peers -- `InvalidAnnounceException`: Protocol violation \ No newline at end of file diff --git a/src/Domain/Tracker/Model/README.md b/src/Domain/Tracker/Model/README.md deleted file mode 100644 index 124456276..000000000 --- a/src/Domain/Tracker/Model/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Tracker Domain Models - -Contains aggregate roots and entities for the BitTorrent tracker: - -- `Torrent`: Aggregate root for torrent management -- `Peer`: Entity representing a BitTorrent peer -- `TorrentStatistics`: Value object for torrent stats - -Example: - -```php -class Torrent extends AggregateRoot -{ - public function announce(Peer $peer, AnnounceEvent $event): void - { - // Business logic for handling announces - } -} -``` \ No newline at end of file diff --git a/src/Domain/Tracker/Repository/README.md b/src/Domain/Tracker/Repository/README.md deleted file mode 100644 index 3745d6e4e..000000000 --- a/src/Domain/Tracker/Repository/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Tracker Repository Interfaces - -Repository interfaces for tracker aggregates: - -- `TorrentRepositoryInterface`: Torrent aggregate persistence -- `PeerRepositoryInterface`: Peer data access -- `TrackerStatsRepositoryInterface`: Statistics queries - -These define persistence contracts without implementation details. \ No newline at end of file diff --git a/src/Domain/Tracker/ValueObject/README.md b/src/Domain/Tracker/ValueObject/README.md deleted file mode 100644 index cd3fcf0ee..000000000 --- a/src/Domain/Tracker/ValueObject/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Tracker Value Objects - -Immutable objects representing domain concepts: - -- `InfoHash`: 20-byte torrent identifier -- `PeerId`: Peer client identifier -- `Port`: Network port (1-65535) -- `BytesTransferred`: Upload/download bytes - -Example: - -```php -final class InfoHash -{ - private string $hash; - - public function __construct(string $hash) - { - $this->guardAgainstInvalidHash($hash); - $this->hash = $hash; - } -} -``` \ No newline at end of file diff --git a/src/Domain/User/Exception/README.md b/src/Domain/User/Exception/README.md deleted file mode 100644 index f689a7943..000000000 --- a/src/Domain/User/Exception/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# User Domain Exceptions - -Domain-specific exceptions for user operations: - -- `UserNotFoundException`: User not found -- `DuplicateUsernameException`: Username already taken -- `InvalidCredentialsException`: Authentication failed -- `UserNotActivatedException`: Account not activated -- `PasswordComplexityException`: Weak password \ No newline at end of file diff --git a/src/Domain/User/Model/README.md b/src/Domain/User/Model/README.md deleted file mode 100644 index 2001b3ebd..000000000 --- a/src/Domain/User/Model/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# User Domain Models - -Contains user management aggregates and entities: - -- `User`: User aggregate root -- `UserProfile`: User profile information -- `UserCredentials`: Authentication credentials -- `UserPermissions`: Authorization rules - -Business rules: - -- Password complexity requirements -- Username uniqueness -- Email verification workflow -- Account activation/deactivation \ No newline at end of file diff --git a/src/Domain/User/Repository/README.md b/src/Domain/User/Repository/README.md deleted file mode 100644 index 9d4c463c7..000000000 --- a/src/Domain/User/Repository/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# User Repository Interface - -Repository interface for user aggregate: - -- `UserRepositoryInterface`: User persistence and retrieval - - `findById(UserId $id): ?User` - - `findByUsername(Username $username): ?User` - - `findByEmail(Email $email): ?User` - - `save(User $user): void` - - `delete(User $user): void` \ No newline at end of file diff --git a/src/Domain/User/ValueObject/README.md b/src/Domain/User/ValueObject/README.md deleted file mode 100644 index 79c930883..000000000 --- a/src/Domain/User/ValueObject/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# User Value Objects - -Immutable objects for user domain: - -- `UserId`: Unique user identifier -- `Username`: Username with validation -- `Email`: Email address with format validation -- `HashedPassword`: Secure password representation -- `UserRole`: User role enumeration - -These ensure data integrity and type safety. \ No newline at end of file diff --git a/src/Infrastructure/Cache/File/README.md b/src/Infrastructure/Cache/File/README.md deleted file mode 100644 index 9f781c1b9..000000000 --- a/src/Infrastructure/Cache/File/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# File Cache Adapter - -File system-based caching implementation: - -- Simple file-based storage -- Suitable for single-server deployments -- Directory structure optimization -- Lock file support for concurrency -- Automatic garbage collection \ No newline at end of file diff --git a/src/Infrastructure/Cache/Memcached/README.md b/src/Infrastructure/Cache/Memcached/README.md deleted file mode 100644 index 2e60b7e0b..000000000 --- a/src/Infrastructure/Cache/Memcached/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Memcached Cache Adapter - -Memcached-based caching implementation: - -- Simple key-value caching -- Distributed cache support -- Automatic key expiration -- Connection pooling -- Consistent hashing for distribution \ No newline at end of file diff --git a/src/Infrastructure/Cache/Redis/README.md b/src/Infrastructure/Cache/Redis/README.md deleted file mode 100644 index da7e090aa..000000000 --- a/src/Infrastructure/Cache/Redis/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Redis Cache Adapter - -Redis-based caching implementation: - -- High-performance key-value storage -- Support for data structures (lists, sets, hashes) -- TTL management -- Pub/sub for cache invalidation -- Cluster support for scalability \ No newline at end of file diff --git a/src/Infrastructure/DependencyInjection/README.md b/src/Infrastructure/DependencyInjection/README.md deleted file mode 100644 index e65de5a19..000000000 --- a/src/Infrastructure/DependencyInjection/README.md +++ /dev/null @@ -1,171 +0,0 @@ -# Dependency Injection Infrastructure - -This directory contains the dependency injection container implementation using PHP-DI, following hexagonal architecture principles. - -## Architecture Overview - -The DI container is placed in the Infrastructure layer because: -- Dependency injection is a technical implementation detail -- It handles wiring and bootstrapping, not business logic -- The domain layer remains pure without framework dependencies - -## Structure - -``` -DependencyInjection/ -โ”œโ”€โ”€ Container.php # Wrapper around PHP-DI container -โ”œโ”€โ”€ ContainerFactory.php # Factory for building configured containers -โ”œโ”€โ”€ Bootstrap.php # Application bootstrapper -โ””โ”€โ”€ Definitions/ # Service definitions by layer - โ”œโ”€โ”€ DomainDefinitions.php - โ”œโ”€โ”€ ApplicationDefinitions.php - โ”œโ”€โ”€ InfrastructureDefinitions.php - โ””โ”€โ”€ PresentationDefinitions.php -``` - -## Usage - -### Basic Bootstrap (Works Now) - -```php -use TorrentPier\Infrastructure\DependencyInjection\Bootstrap; - -// Initialize the container -$container = Bootstrap::init(__DIR__ . '/../..'); - -// Basic usage -$containerInstance = app(); // Get container itself -$hasService = $container->has('some.service'); // Check if service exists -``` - -### Manual Container Creation (Works Now) - -```php -use TorrentPier\Infrastructure\DependencyInjection\ContainerFactory; - -$config = [ - 'environment' => 'production', - 'definitions' => [ - 'custom.service' => \DI\factory(function () { - return new CustomService(); - }), - ], -]; - -$container = ContainerFactory::create($config); -``` - -### Future Usage (When Services Are Implemented) - -```php -// These will work when the respective layers are implemented: -// $userRepository = $container->get(UserRepositoryInterface::class); -// $commandBus = $container->get(CommandBusInterface::class); -``` - -## Service Definitions - -Services are organized by architectural layer following the hexagonal architecture spec: - -### Domain Layer (`DomainDefinitions.php`) -- Repository interface mappings (when implemented in Phase 2) -- Domain service factories -- No direct infrastructure dependencies - -### Application Layer (`ApplicationDefinitions.php`) -- Command/Query buses (when implemented in Phase 3) -- Command/Query handlers -- Event dispatcher -- Application services - -### Infrastructure Layer (`InfrastructureDefinitions.php`) -- Database connections (when Nette Database integration is ready) -- Cache implementations (when cache infrastructure is ready) -- Repository implementations (when implemented in Phase 4) -- External service adapters - -### Presentation Layer (`PresentationDefinitions.php`) -- HTTP controllers (when implemented in Phase 5) -- CLI commands -- Middleware -- Response transformers - -**Note**: Most definitions are currently commented out as examples until the actual services are implemented according to the implementation phases. - -## Configuration - -Configuration is loaded from multiple sources: - -1. **Environment Variables** (`.env` file) -2. **Configuration Files** (`/config/*.php`) -3. **Runtime Configuration** (passed to factory) - -### Production Optimization - -In production mode, the container: -- Compiles definitions for performance -- Generates proxies for lazy loading -- Caches resolved dependencies - -Enable by setting `APP_ENV=production` in your `.env` file. - -## Best Practices - -1. **Use Interfaces**: Define interfaces in domain, implement in infrastructure -2. **Explicit Definitions**: Prefer explicit over magic for complex services -3. **Layer Separation**: Keep definitions organized by architectural layer -4. **Lazy Loading**: Use factories for expensive services -5. **Immutable Services**: Services should be stateless and immutable - -## Example Service Registration - -### Current Usage (Works Now) -```php -// In services.php or custom definitions -return [ - 'custom.service' => \DI\factory(function () { - return new CustomService(); - }), - - 'test.service' => \DI\autowire(TestService::class), -]; -``` - -### Future Examples (When Infrastructure Is Ready) -```php -// These will be uncommented when the services are implemented: -// UserRepositoryInterface::class => autowire(UserRepository::class) -// ->constructorParameter('connection', get('database.connection.default')) -// ->constructorParameter('cache', get('cache.factory')), -// -// 'email.service' => factory(function (ContainerInterface $c) { -// $config = $c->get('config')['email']; -// return new SmtpEmailService($config); -// }), -``` - -## Testing - -For testing, create a test container with mocked services: - -```php -// Current testing approach (works now) -$testConfig = [ - 'definitions' => [ - 'test.service' => \DI\factory(function () { - return new MockTestService(); - }), - ], - 'environment' => 'testing', -]; - -$container = ContainerFactory::create($testConfig); - -// Future testing (when services are implemented) -// $testConfig = [ -// 'definitions' => [ -// UserRepositoryInterface::class => $mockUserRepository, -// EmailServiceInterface::class => $mockEmailService, -// ], -// ]; -``` diff --git a/src/Infrastructure/Email/Template/README.md b/src/Infrastructure/Email/Template/README.md deleted file mode 100644 index 3497a96ba..000000000 --- a/src/Infrastructure/Email/Template/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Email Templates - -Email template management: - -- HTML and plain text templates -- Template variables and placeholders -- Multi-language support -- Template caching -- Preview functionality - -Common templates: - -- User registration confirmation -- Password reset -- Torrent notifications -- Forum post notifications \ No newline at end of file diff --git a/src/Infrastructure/Email/Transport/README.md b/src/Infrastructure/Email/Transport/README.md deleted file mode 100644 index 0de823895..000000000 --- a/src/Infrastructure/Email/Transport/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Email Transport - -Email delivery mechanisms: - -- `SmtpTransport`: Traditional SMTP delivery -- `SendgridTransport`: SendGrid API integration -- `MailgunTransport`: Mailgun API integration -- `NullTransport`: Testing/development transport - -Supports queuing and retry mechanisms. \ No newline at end of file diff --git a/src/Infrastructure/FileStorage/Local/README.md b/src/Infrastructure/FileStorage/Local/README.md deleted file mode 100644 index cb0fdac49..000000000 --- a/src/Infrastructure/FileStorage/Local/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Local File Storage - -Local filesystem storage implementation: - -- Direct disk storage -- Directory structure management -- File permissions handling -- Atomic file operations -- Cleanup and maintenance - -Used for torrent files, avatars, and attachments. \ No newline at end of file diff --git a/src/Infrastructure/FileStorage/S3/README.md b/src/Infrastructure/FileStorage/S3/README.md deleted file mode 100644 index c008d9f5a..000000000 --- a/src/Infrastructure/FileStorage/S3/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# S3 File Storage - -Amazon S3 (or compatible) storage implementation: - -- Cloud object storage -- Pre-signed URLs for direct uploads -- CDN integration support -- Lifecycle policies -- Multi-region support - -Provides scalable storage for large deployments. \ No newline at end of file diff --git a/src/Infrastructure/Persistence/Database/README.md b/src/Infrastructure/Persistence/Database/README.md deleted file mode 100644 index c250b60e8..000000000 --- a/src/Infrastructure/Persistence/Database/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Database Adapter - -Database connection and query building layer: - -- `DatabaseAdapterInterface`: Common database operations -- `QueryBuilder`: Fluent query construction -- `ConnectionPool`: Connection management - -Provides abstraction over raw database access. diff --git a/src/Infrastructure/Persistence/Migration/README.md b/src/Infrastructure/Persistence/Migration/README.md deleted file mode 100644 index 1f203e645..000000000 --- a/src/Infrastructure/Persistence/Migration/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Database Migrations - -Schema version control and migration management: - -- Migration files for schema changes -- Seed data for initial setup -- Rollback support -- Migration history tracking - -Uses Phinx or similar migration tool. \ No newline at end of file diff --git a/src/Infrastructure/Persistence/Repository/README.md b/src/Infrastructure/Persistence/Repository/README.md deleted file mode 100644 index 002760d4a..000000000 --- a/src/Infrastructure/Persistence/Repository/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Repository Implementations - -Concrete implementations of domain repository interfaces: - -- Uses database adapter for persistence -- Implements caching strategies -- Handles query optimization -- Supports multiple database backends - -Example: - -```php -class TorrentRepository implements TorrentRepositoryInterface -{ - public function __construct( - private DatabaseAdapterInterface $db - ) {} - - public function findByInfoHash(InfoHash $infoHash): ?Torrent - { - // Database adapter implementation - $row = $this->db->select('torrents') - ->where('info_hash', $infoHash->toString()) - ->first(); - - return $row ? $this->hydrateFromRow($row) : null; - } -} -``` \ No newline at end of file diff --git a/src/Infrastructure/README.md b/src/Infrastructure/README.md deleted file mode 100644 index 2098a9f97..000000000 --- a/src/Infrastructure/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Infrastructure Layer - -Technical implementations and external service adapters: - -- Database persistence -- Caching mechanisms -- Email services -- File storage -- Third-party integrations - -Infrastructure depends on domain, not vice versa. \ No newline at end of file diff --git a/src/Presentation/Cli/Commands/README.md b/src/Presentation/Cli/Commands/README.md deleted file mode 100644 index 5e2cf1239..000000000 --- a/src/Presentation/Cli/Commands/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# CLI Commands - -Console commands for administrative tasks: - -- `UserCreateCommand`: Create users from CLI -- `CacheClearCommand`: Clear cache stores -- `MigrateCommand`: Run database migrations -- `CronCommand`: Execute scheduled tasks - -Built using Symfony Console component. \ No newline at end of file diff --git a/src/Presentation/Http/Controllers/Admin/README.md b/src/Presentation/Http/Controllers/Admin/README.md deleted file mode 100644 index 6e478949b..000000000 --- a/src/Presentation/Http/Controllers/Admin/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# Admin Panel Controllers - -Administrative interface controllers with enhanced security: - -- Role-based access control (RBAC) -- Audit logging for all actions -- Additional authentication checks -- Administrative dashboards and reports - -Example: - -```php -class AdminUserController -{ - public function index(Request $request): Response - { - $query = new GetUsersQuery( - page: $request->getPage(), - filters: $request->getFilters() - ); - - $users = $this->queryBus->handle($query); - - return $this->render('admin/users/index', [ - 'users' => $users, - 'filters' => $request->getFilters() - ]); - } -} -``` \ No newline at end of file diff --git a/src/Presentation/Http/Controllers/Api/README.md b/src/Presentation/Http/Controllers/Api/README.md deleted file mode 100644 index 7d944db2e..000000000 --- a/src/Presentation/Http/Controllers/Api/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# API Controllers - -RESTful API endpoints following OpenAPI specification: - -- JSON request/response format -- Proper HTTP status codes -- HATEOAS where applicable -- Rate limiting aware - -Example: - -```php -class UserController -{ - public function register(RegisterRequest $request): JsonResponse - { - $command = new RegisterUserCommand( - $request->getUsername(), - $request->getEmail(), - $request->getPassword() - ); - - $userId = $this->commandBus->handle($command); - - return new JsonResponse([ - 'id' => $userId, - 'username' => $request->getUsername() - ], Response::HTTP_CREATED); - } -} -``` \ No newline at end of file diff --git a/src/Presentation/Http/Controllers/Web/README.md b/src/Presentation/Http/Controllers/Web/README.md deleted file mode 100644 index 015c2d19c..000000000 --- a/src/Presentation/Http/Controllers/Web/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Web Controllers - -Traditional web interface controllers: - -- HTML response generation -- Template rendering -- Form handling -- Session management -- CSRF protection - -Controllers for: - -- Forum browsing and posting -- Torrent browsing and downloading -- User profiles and settings -- Search functionality \ No newline at end of file diff --git a/src/Presentation/Http/Middleware/README.md b/src/Presentation/Http/Middleware/README.md deleted file mode 100644 index c5a1d1a0c..000000000 --- a/src/Presentation/Http/Middleware/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# HTTP Middleware - -Request/response pipeline middleware: - -- `AuthenticationMiddleware`: User authentication -- `AuthorizationMiddleware`: Permission checks -- `CsrfProtectionMiddleware`: CSRF token validation -- `RateLimitMiddleware`: Request throttling -- `LocalizationMiddleware`: Language detection -- `CorsMiddleware`: Cross-origin resource sharing - -Middleware follows PSR-15 standard. \ No newline at end of file diff --git a/src/Presentation/Http/Requests/README.md b/src/Presentation/Http/Requests/README.md deleted file mode 100644 index 8e0a47d1c..000000000 --- a/src/Presentation/Http/Requests/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# HTTP Requests - -Request objects and validation: - -- Request DTOs with validation rules -- Type-safe access to request data -- File upload handling -- Input sanitization -- Custom validation rules - -Example: - -```php -class RegisterRequest extends FormRequest -{ - public function rules(): array - { - return [ - 'username' => ['required', 'string', 'min:3', 'max:20', 'unique:users'], - 'email' => ['required', 'email', 'unique:users'], - 'password' => ['required', 'string', 'min:8', 'confirmed'], - ]; - } -} -``` \ No newline at end of file diff --git a/src/Presentation/Http/Responses/README.md b/src/Presentation/Http/Responses/README.md deleted file mode 100644 index bce8c8325..000000000 --- a/src/Presentation/Http/Responses/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# HTTP Responses - -Response transformation and formatting: - -- Response factories -- JSON transformers -- View presenters -- Error response formatting -- Content negotiation - -Ensures consistent API responses and proper HTTP semantics. \ No newline at end of file diff --git a/src/Presentation/README.md b/src/Presentation/README.md deleted file mode 100644 index 3e99a424b..000000000 --- a/src/Presentation/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Presentation Layer - -User interface implementations: - -- HTTP controllers for web and API -- CLI commands for console operations -- Request/response handling -- Input validation -- Output formatting - -This layer translates between external format and application format. \ No newline at end of file diff --git a/src/Whoops/README.md b/src/Whoops/README.md deleted file mode 100644 index 572b42980..000000000 --- a/src/Whoops/README.md +++ /dev/null @@ -1,132 +0,0 @@ -# TorrentPier Whoops Enhanced Error Reporting - -This directory contains enhanced Whoops error handlers specifically designed for TorrentPier to provide better debugging information when database errors occur. - -## Features - -### Enhanced Database Error Reporting - -The enhanced Whoops handlers provide comprehensive database information when errors occur: - -1. **Current SQL Query** - Shows the exact query that caused the error -2. **Recent Query History** - Displays the last 5 SQL queries executed -3. **Database Connection Status** - Connection state, server info, database name -4. **Error Context** - PDO error codes, exception details, source location -5. **TorrentPier Environment** - Debug mode status, system information - -### Components - -#### EnhancedPrettyPageHandler - -Extends Whoops' default `PrettyPageHandler` to include: -- **Database Information** table with connection details and current query -- **Recent SQL Queries** table showing query history with timing -- **TorrentPier Environment** table with system status - -#### DatabaseErrorHandler - -Specialized handler that: -- Adds database context to exception stack frames -- Identifies database-related code in the call stack -- Collects comprehensive database state information -- Formats SQL queries for readable display - -## Usage - -The enhanced handlers are automatically activated when `DBG_USER` is enabled in TorrentPier configuration. - -### Automatic Integration - -```php -// In src/Dev.php - automatically configured -$prettyPageHandler = new \TorrentPier\Whoops\EnhancedPrettyPageHandler(); -``` - -### Database Error Logging - -Database errors are now automatically logged even when they occur in Nette Database layer: - -```php -// Enhanced error handling in Database.php -try { - $row = $result->fetch(); -} catch (\Exception $e) { - // Log the error including the query that caused it - $this->debugger->log_error($e); - throw $e; // Re-throw for Whoops display -} -``` - -## Error Information Displayed - -When a database error occurs, Whoops will now show: - -### Database Information -- Connection Status: Connected/Disconnected -- Database Server: Host and port information -- Selected Database: Current database name -- Database Engine: MySQL/PostgreSQL/etc. -- Total Queries: Number of queries executed -- Total Query Time: Cumulative execution time -- Current Query: The SQL that caused the error -- Last Database Error: Error code and message -- PDO Driver: Database driver information -- Server Version: Database server version - -### Recent SQL Queries -- **Query #1-5**: Last 5 queries executed - - SQL: Formatted query text - - Time: Execution time in seconds - - Source: File and line where query originated - - Info: Additional query information - - Memory: Memory usage if available - -### TorrentPier Environment -- Application Environment: local/production/etc. -- Debug Mode: Enabled/Disabled -- SQL Debug: Enabled/Disabled -- TorrentPier Version: Current version -- Config Loaded: Configuration status -- Cache System: Availability status -- Language System: Status and encoding -- Template System: Twig-based availability -- Execution Time: Request processing time -- Peak Memory: Maximum memory used -- Current Memory: Current memory usage -- Request Method: GET/POST/etc. -- Request URI: Current page -- User Agent: Browser information -- Remote IP: Client IP address - -## Configuration - -The enhanced handlers respect TorrentPier's debug configuration: - -- `DBG_USER`: Must be enabled to show enhanced error pages -- `SQL_DEBUG`: Enables SQL query logging and timing -- `APP_ENV`: Determines environment-specific features - -## Logging - -Database errors are now logged in multiple locations: - -1. **PHP Error Log**: Basic error message -2. **TorrentPier bb_log**: Detailed error with context (`database_errors.log`) -3. **Whoops Log**: Complete error details (`php_whoops.log`) - -## Security - -The enhanced handlers maintain security by: -- Only showing detailed information when `DBG_USER` is enabled -- Using Whoops' blacklist for sensitive data -- Logging detailed information to files (not user-accessible) -- Providing generic error messages to non-debug users - -## Integration with TorrentPier 3.0 - -All enhancements are: - -- **Integrated** with the new TorrentPier 3.0 architecture -- **Modern** - designed for the rewritten codebase -- **Optional** - only activated in debug mode -- **Safe** - no security implications for production use diff --git a/tests/Feature/ContainerIntegrationTest.php b/tests/Feature/ContainerIntegrationTest.php deleted file mode 100644 index d972ee3ef..000000000 --- a/tests/Feature/ContainerIntegrationTest.php +++ /dev/null @@ -1,193 +0,0 @@ -createTestRootDirectory(); - $this->createTestConfigFiles($rootPath, [ - 'services' => [ - 'integration.test' => 'integration_value', - ], - ]); - - $container = Bootstrap::init($rootPath); - - expect($container)->toBeInstanceOf(Container::class); - expect($container->get('integration.test'))->toBe('integration_value'); - - removeTempDirectory($rootPath); - }); - - it('integrates with global helper functions', function () { - $rootPath = $this->createTestRootDirectory(); - $this->createTestConfigFiles($rootPath, [ - 'services' => [ - 'helper.test' => 'helper_value', - ], - ]); - - Bootstrap::init($rootPath); - - // Test container() helper - expect(container())->toBeInstanceOf(Container::class); - - // Test app() helper without parameter - expect(app())->toBeInstanceOf(Container::class); - - // Test app() helper with service ID - expect(app('helper.test'))->toBe('helper_value'); - - removeTempDirectory($rootPath); - }); - - it('handles missing services gracefully in helpers', function () { - $rootPath = $this->createTestRootDirectory(); - Bootstrap::init($rootPath); - - // Should throw RuntimeException for missing service - expect(fn() => app('missing.service')) - ->toThrow(RuntimeException::class) - ->toThrow('not found in container'); - - removeTempDirectory($rootPath); - }); - - it('supports autowiring for simple classes', function () { - $rootPath = $this->createTestRootDirectory(); - $container = Bootstrap::init($rootPath); - - // Should be able to autowire stdClass - expect($container->has(stdClass::class))->toBeTrue(); - expect($container->get(stdClass::class))->toBeInstanceOf(stdClass::class); - - removeTempDirectory($rootPath); - }); - - it('loads all architectural layer definitions', function () { - $rootPath = $this->createTestRootDirectory(); - $container = Bootstrap::init($rootPath); - - // Container should be created successfully with all layer definitions loaded - // Even though most definitions are commented out, the loading should work - expect($container)->toBeInstanceOf(Container::class); - - // Container should have itself registered - expect($container->get(Container::class))->toBe($container); - expect($container->get('container'))->toBe($container); - - removeTempDirectory($rootPath); - }); - - it('supports environment-based configuration', function () { - $rootPath = $this->createTestRootDirectory(); - $this->createTestConfigFiles($rootPath, [ - 'container' => [ - 'environment' => 'production', - 'compilation_dir' => $rootPath . '/internal_data/cache/container', - 'proxies_dir' => $rootPath . '/internal_data/cache/proxies', - ], - ]); - - $container = Bootstrap::init($rootPath); - - expect($container)->toBeInstanceOf(Container::class); - - removeTempDirectory($rootPath); - }); - - it('supports service provider registration', function () { - $testProviderClass = new class implements \TorrentPier\Infrastructure\DependencyInjection\ServiceProvider { - public static bool $wasRegistered = false; - public static bool $wasBooted = false; - - public function register(\TorrentPier\Infrastructure\DependencyInjection\Container $container): void - { - self::$wasRegistered = true; - $container->getWrappedContainer()->set('provider.test', 'provider_registered'); - } - - public function boot(\TorrentPier\Infrastructure\DependencyInjection\Container $container): void - { - self::$wasBooted = true; - } - }; - - $rootPath = $this->createTestRootDirectory(); - $this->createTestConfigFiles($rootPath, [ - 'container' => [ - 'providers' => [get_class($testProviderClass)], - ], - ]); - - $container = Bootstrap::init($rootPath); - - expect($testProviderClass::$wasRegistered)->toBeTrue(); - expect($testProviderClass::$wasBooted)->toBeTrue(); - expect($container->get('provider.test'))->toBe('provider_registered'); - - removeTempDirectory($rootPath); - }); - - it('handles configuration file loading priority', function () { - $rootPath = $this->createTestRootDirectory(); - $this->createTestConfigFiles($rootPath, [ - 'services' => [ - 'priority.test' => \DI\factory(function () { - return 'from_services_file'; - }), - ], - ]); - - // Initialize with runtime config that should override file config - $container = Bootstrap::init($rootPath, [ - 'definitions' => [ - 'priority.test' => \DI\factory(function () { - return 'from_runtime_config'; - }), - 'runtime.only' => \DI\factory(function () { - return 'runtime_value'; - }), - ], - ]); - - // Runtime config should override file config - expect($container->get('priority.test'))->toBe('from_runtime_config'); - expect($container->get('runtime.only'))->toBe('runtime_value'); - - removeTempDirectory($rootPath); - }); - - it('provides meaningful error messages', function () { - $rootPath = $this->createTestRootDirectory(); - Bootstrap::init($rootPath); - - try { - app('definitely.missing.service'); - fail('Expected exception to be thrown'); - } catch (RuntimeException $e) { - expect($e->getMessage())->toContain('definitely.missing.service'); - expect($e->getMessage())->toContain('not found in container'); - } - - removeTempDirectory($rootPath); - }); - - it('supports performance measurement', function () { - $rootPath = $this->createTestRootDirectory(); - - $time = measureExecutionTime(function () use ($rootPath) { - Bootstrap::init($rootPath); - }); - - // Container initialization should be reasonably fast - expect($time)->toBeLessThan(1.0); // Should take less than 1 second - - removeTempDirectory($rootPath); - }); -}); diff --git a/tests/Pest.php b/tests/Pest.php deleted file mode 100644 index f1c9a7613..000000000 --- a/tests/Pest.php +++ /dev/null @@ -1,95 +0,0 @@ -extend(Tests\TestCase::class)->in('Feature'); -pest()->extend(Tests\TestCase::class)->in('Unit'); - -/* -|-------------------------------------------------------------------------- -| Expectations -|-------------------------------------------------------------------------- -| -| When you're writing tests, you often need to check that values meet certain conditions. The -| "expect()" function gives you access to a set of "expectations" methods that you can use -| to assert different things. Of course, you may extend the Expectation API at any time. -| -*/ - -expect()->extend('toBeOne', function () { - return $this->toBe(1); -}); - -/* -|-------------------------------------------------------------------------- -| Functions -|-------------------------------------------------------------------------- -| -| While Pest is very powerful out-of-the-box, you may have some testing code specific to your -| project that you don't want to repeat in every file. Here you can also expose helpers as -| global functions to help you to reduce the number of lines of code in your test files. -| -*/ - -/** - * Performance Testing Helpers - */ -function measureExecutionTime(callable $callback): float -{ - $start = microtime(true); - $callback(); - return microtime(true) - $start; -} - -function expectExecutionTimeUnder(callable $callback, float $maxSeconds): void -{ - $time = measureExecutionTime($callback); - expect($time)->toBeLessThan($maxSeconds, "Execution took {$time}s, expected under {$maxSeconds}s"); -} - -/** - * File System Helpers - */ -function createTempDirectory(): string -{ - $tempDir = sys_get_temp_dir() . '/torrentpier_test_' . uniqid(); - mkdir($tempDir, 0755, true); - return $tempDir; -} - -function removeTempDirectory(string $dir): void -{ - if (is_dir($dir)) { - $files = array_diff(scandir($dir), ['.', '..']); - foreach ($files as $file) { - $path = $dir . '/' . $file; - is_dir($path) ? removeTempDirectory($path) : unlink($path); - } - rmdir($dir); - } -} - -/** - * Exception Testing Helpers - */ -function expectException(callable $callback, string $exceptionClass, ?string $message = null): void -{ - try { - $callback(); - fail("Expected exception $exceptionClass was not thrown"); - } catch (Throwable $e) { - expect($e)->toBeInstanceOf($exceptionClass); - if ($message) { - expect($e->getMessage())->toContain($message); - } - } -} diff --git a/tests/README.md b/tests/README.md deleted file mode 100644 index 1ec09dc6b..000000000 --- a/tests/README.md +++ /dev/null @@ -1,433 +0,0 @@ -# ๐Ÿงช TorrentPier 3.0 Testing Infrastructure - -This document outlines the testing infrastructure for TorrentPier 3.0, built using **Pest PHP** and following the hexagonal architecture principles outlined in the project specification. - -## ๐Ÿ“– Table of Contents - -- [Overview](#overview) -- [Hexagonal Architecture Testing](#hexagonal-architecture-testing) -- [Test Organization](#test-organization) -- [DI Container Testing](#di-container-testing) -- [Testing Patterns](#testing-patterns) -- [Test Execution](#test-execution) -- [Best Practices](#best-practices) - -## ๐ŸŽฏ Overview - -TorrentPier 3.0's testing suite is designed following the hexagonal architecture testing strategy: - -- **Domain**: Pure unit tests, no mocks needed -- **Application**: Unit tests with mocked repositories -- **Infrastructure**: Integration tests with real services -- **Presentation**: E2E tests for user journeys - -### Core Testing Principles - -1. **Architecture-Driven**: Tests follow the hexagonal architecture layers -2. **Phase-Aligned**: Testing matches the 5-phase implementation strategy -3. **Clean Slate**: No legacy dependencies, modern PHP 8.3+ testing -4. **Infrastructure First**: Focus on foundational DI container testing -5. **Future-Ready**: Structure prepared for upcoming domain/application layers - -## ๐Ÿ—๏ธ Hexagonal Architecture Testing - -### Testing Strategy by Layer - -#### Domain Layer Testing (Phase 2 - Future) -```php -// Pure unit tests, no framework dependencies -it('validates business rules without external dependencies', function () { - $user = new User(new UserId(1), new Email('test@example.com')); - expect($user->canPost())->toBeTrue(); -}); -``` - -#### Application Layer Testing (Phase 3 - Future) -```php -// Unit tests with mocked repositories -it('handles user registration command', function () { - $mockRepo = Mockery::mock(UserRepositoryInterface::class); - $handler = new RegisterUserHandler($mockRepo); - - $command = new RegisterUserCommand('john', 'john@example.com'); - $handler->handle($command); - - $mockRepo->shouldHaveReceived('save'); -}); -``` - -#### Infrastructure Layer Testing (Phase 1 - Current) -```php -// Integration tests with real services -it('creates container with real PHP-DI integration', function () { - $container = ContainerFactory::create(); - expect($container)->toBeInstanceOf(Container::class); -}); -``` - -#### Presentation Layer Testing (Phase 5 - Future) -```php -// E2E tests for user journeys -it('handles API request end-to-end', function () { - $response = $this->post('/api/users', ['name' => 'John']); - expect($response->status())->toBe(201); -}); -``` - -## ๐Ÿ“ Test Organization - -### Directory Structure - -``` -tests/ -โ”œโ”€โ”€ README.md # This documentation -โ”œโ”€โ”€ Pest.php # Clean Pest configuration -โ”œโ”€โ”€ TestCase.php # Enhanced base test case with DI utilities -โ”œโ”€โ”€ Unit/Infrastructure/DependencyInjection/ # DI Container tests (Phase 1) -โ”‚ โ”œโ”€โ”€ ContainerTest.php # Container wrapper tests -โ”‚ โ”œโ”€โ”€ ContainerFactoryTest.php # Factory functionality tests -โ”‚ โ”œโ”€โ”€ BootstrapTest.php # Application bootstrapping tests -โ”‚ โ”œโ”€โ”€ ServiceProviderTest.php # Service provider interface tests -โ”‚ โ””โ”€โ”€ Definitions/ # Layer-specific definition tests -โ”‚ โ”œโ”€โ”€ DomainDefinitionsTest.php -โ”‚ โ”œโ”€โ”€ ApplicationDefinitionsTest.php -โ”‚ โ”œโ”€โ”€ InfrastructureDefinitionsTest.php -โ”‚ โ””โ”€โ”€ PresentationDefinitionsTest.php -โ””โ”€โ”€ Feature/ # Integration tests - โ””โ”€โ”€ ContainerIntegrationTest.php # End-to-end container tests -``` - -### Future Structure (As Phases Are Implemented) - -``` -tests/ -โ”œโ”€โ”€ Unit/ -โ”‚ โ”œโ”€โ”€ Domain/ # Phase 2: Pure business logic tests -โ”‚ โ”‚ โ”œโ”€โ”€ User/ -โ”‚ โ”‚ โ”œโ”€โ”€ Forum/ -โ”‚ โ”‚ โ””โ”€โ”€ Tracker/ -โ”‚ โ”œโ”€โ”€ Application/ # Phase 3: Use case orchestration tests -โ”‚ โ”‚ โ”œโ”€โ”€ User/ -โ”‚ โ”‚ โ”œโ”€โ”€ Forum/ -โ”‚ โ”‚ โ””โ”€โ”€ Tracker/ -โ”‚ โ”œโ”€โ”€ Infrastructure/ # Phase 4: External service integration tests -โ”‚ โ”‚ โ”œโ”€โ”€ Persistence/ -โ”‚ โ”‚ โ”œโ”€โ”€ Cache/ -โ”‚ โ”‚ โ””โ”€โ”€ Email/ -โ”‚ โ””โ”€โ”€ Presentation/ # Phase 5: Interface layer tests -โ”‚ โ”œโ”€โ”€ Http/ -โ”‚ โ””โ”€โ”€ Cli/ -โ””โ”€โ”€ Feature/ # Cross-layer integration tests -``` - -## ๐Ÿ› ๏ธ DI Container Testing - -### Current Implementation (Phase 1) - -The DI container is the foundation of TorrentPier 3.0's architecture. Our tests ensure: - -#### Container Wrapper Testing -```php -// tests/Unit/Infrastructure/DependencyInjection/ContainerTest.php -it('implements PSR-11 ContainerInterface', function () { - expect($this->container)->toBeInstanceOf(\Psr\Container\ContainerInterface::class); -}); - -it('can resolve autowired classes', function () { - $result = $this->container->get(stdClass::class); - expect($result)->toBeInstanceOf(stdClass::class); -}); - -it('throws NotFoundExceptionInterface for non-existent services', function () { - expect(fn() => $this->container->get('non.existent.service')) - ->toThrow(NotFoundExceptionInterface::class); -}); -``` - -#### Factory Configuration Testing -```php -// tests/Unit/Infrastructure/DependencyInjection/ContainerFactoryTest.php -it('applies configuration correctly', function () { - $config = [ - 'environment' => 'testing', - 'autowiring' => true, - 'definitions' => [ - 'test.service' => \DI\factory(fn() => 'test_value'), - ], - ]; - - $container = ContainerFactory::create($config); - expect($container->get('test.service'))->toBe('test_value'); -}); -``` - -#### Bootstrap Integration Testing -```php -// tests/Unit/Infrastructure/DependencyInjection/BootstrapTest.php -it('loads configuration from multiple sources', function () { - $rootPath = $this->createTestRootDirectory(); - $this->createTestConfigFiles($rootPath, [ - 'env' => ['APP_ENV' => 'testing'], - 'services' => ['config.service' => \DI\factory(fn() => 'merged_config')], - ]); - - $container = Bootstrap::init($rootPath); - expect($container->get('config.service'))->toBe('merged_config'); -}); -``` - -### Test Utilities - -#### Enhanced TestCase -```php -// tests/TestCase.php -abstract class TestCase extends BaseTestCase -{ - protected function createTestContainer(array $config = []): Container - { - $defaultConfig = [ - 'environment' => 'testing', - 'autowiring' => true, - 'definitions' => [], - ]; - - return ContainerFactory::create(array_merge($defaultConfig, $config)); - } - - protected function assertCanResolve(Container $container, string $serviceId): void - { - $this->assertTrue($container->has($serviceId)); - $this->assertNotNull($container->get($serviceId)); - } -} -``` - -## ๐ŸŽจ Testing Patterns - -### 1. Infrastructure Integration Testing -```php -// Real service integration (current phase) -it('integrates with real PHP-DI container', function () { - $container = $this->createTestContainer([ - 'definitions' => [ - 'real.service' => \DI\autowire(stdClass::class), - ], - ]); - - $service = $container->get('real.service'); - expect($service)->toBeInstanceOf(stdClass::class); -}); -``` - -### 2. Configuration-Driven Testing -```php -// Environment-based configuration -it('adapts to different environments', function () { - $prodContainer = $this->createTestContainer(['environment' => 'production']); - $devContainer = $this->createTestContainer(['environment' => 'development']); - - expect($prodContainer)->toBeInstanceOf(Container::class); - expect($devContainer)->toBeInstanceOf(Container::class); -}); -``` - -### 3. Service Provider Testing -```php -// Modular service registration -it('registers services through providers', function () { - $provider = new class implements ServiceProvider { - public function register(Container $container): void { - $container->getWrappedContainer()->set('provider.service', 'registered'); - } - public function boot(Container $container): void {} - }; - - $container = $this->createTestContainer(); - $provider->register($container); - - expect($container->get('provider.service'))->toBe('registered'); -}); -``` - -### 4. Layer Definition Testing -```php -// Architectural layer compliance -it('follows domain layer principles', function () { - $definitions = DomainDefinitions::getDefinitions(); - - // Domain definitions should be empty in Phase 1 - expect($definitions)->toBe([]); - - // Structure should be prepared for Phase 2 - expect($definitions)->toBeArray(); -}); -``` - -## ๐Ÿš€ Test Execution - -### Running Tests - -```bash -# Run all tests -./vendor/bin/pest - -# Run DI container tests specifically -./vendor/bin/pest tests/Unit/Infrastructure/DependencyInjection/ - -# Run integration tests -./vendor/bin/pest tests/Feature/ - -# Run with coverage -./vendor/bin/pest --coverage - -# Run specific test file -./vendor/bin/pest tests/Unit/Infrastructure/DependencyInjection/ContainerTest.php -``` - -### Performance Testing -```bash -# Measure container bootstrap performance -./vendor/bin/pest --filter="performance" - -# Container creation should be fast -expectExecutionTimeUnder(fn() => Bootstrap::init($rootPath), 1.0); -``` - -## ๐Ÿ“‹ Best Practices - -### 1. Phase-Aligned Testing -```php -// Current Phase 1: Test infrastructure only -it('provides foundation for future phases', function () { - $container = $this->createTestContainer(); - - // Infrastructure works now - expect($container)->toBeInstanceOf(Container::class); - - // Ready for future domain services - expect($container->has(stdClass::class))->toBeTrue(); -}); -``` - -### 2. Architecture Compliance -```php -// Ensure clean architectural boundaries -it('keeps domain layer pure', function () { - $definitions = DomainDefinitions::getDefinitions(); - - // Domain should have no infrastructure dependencies - expect($definitions)->toBeArray(); - - // Future domain services will be dependency-free -}); -``` - -### 3. Configuration Testing -```php -// Test multiple configuration sources -it('merges configuration correctly', function () { - $rootPath = $this->createTestRootDirectory(); - $this->createTestConfigFiles($rootPath, [ - 'container' => ['autowiring' => true], - 'services' => ['test.service' => \DI\factory(fn() => 'test')], - ]); - - $container = Bootstrap::init($rootPath, [ - 'definitions' => ['runtime.service' => \DI\factory(fn() => 'runtime')], - ]); - - expect($container->get('test.service'))->toBe('test'); - expect($container->get('runtime.service'))->toBe('runtime'); -}); -``` - -### 4. Error Handling -```php -// Comprehensive error testing -it('provides meaningful error messages', function () { - $container = $this->createTestContainer(); - - try { - $container->get('missing.service'); - fail('Expected exception'); - } catch (RuntimeException $e) { - expect($e->getMessage())->toContain('missing.service'); - expect($e->getMessage())->toContain('not found in container'); - } -}); -``` - -## ๐Ÿ“Š Current Implementation Status - -### โœ… Phase 1 Complete: Infrastructure Foundation - -- **DI Container**: Fully tested container wrapper with PSR-11 compliance -- **Factory Pattern**: Comprehensive configuration and creation testing -- **Bootstrap Process**: Environment loading and configuration merging -- **Service Providers**: Modular service registration interface -- **Helper Functions**: Global container access with proper error handling -- **Layer Definitions**: Prepared structure for all architectural layers - -### ๐Ÿ”„ Testing Coverage - -- **Container Core**: 100% coverage of wrapper functionality -- **Configuration**: All config sources and merging scenarios tested -- **Error Handling**: Complete PSR-11 exception compliance -- **Integration**: End-to-end bootstrap and usage scenarios -- **Performance**: Container creation and resolution timing validation - -### ๐Ÿ”ฎ Future Phase Testing - -As TorrentPier 3.0 phases are implemented: - -#### Phase 2: Domain Layer -```php -// Domain entity testing (future) -it('validates user business rules', function () { - $user = new User(UserId::generate(), new Email('test@example.com')); - expect($user->isActive())->toBeTrue(); -}); -``` - -#### Phase 3: Application Layer -```php -// Command handler testing (future) -it('processes registration command', function () { - $handler = app(RegisterUserHandler::class); - $command = new RegisterUserCommand('john', 'john@example.com'); - - $userId = $handler->handle($command); - expect($userId)->toBeInstanceOf(UserId::class); -}); -``` - -#### Phase 4: Infrastructure Layer -```php -// Repository integration testing (future) -it('persists user through repository', function () { - $repository = app(UserRepositoryInterface::class); - $user = User::create('john', 'john@example.com'); - - $repository->save($user); - expect($repository->findById($user->getId()))->not->toBeNull(); -}); -``` - -#### Phase 5: Presentation Layer -```php -// Controller integration testing (future) -it('handles user registration via API', function () { - $response = $this->postJson('/api/users', [ - 'username' => 'john', - 'email' => 'john@example.com', - ]); - - expect($response->status())->toBe(201); -}); -``` - ---- - -**TorrentPier 3.0 Testing Philosophy**: Tests serve as both validation and documentation of the hexagonal architecture. Each layer has distinct testing strategies that ensure clean separation of concerns and maintainable code. - -For questions about testing patterns or contributions, refer to the [TorrentPier GitHub repository](https://github.com/torrentpier/torrentpier) or the hexagonal architecture specification at `/docs/specs/hexagonal-architecture-spec.md`. \ No newline at end of file diff --git a/tests/TestCase.php b/tests/TestCase.php deleted file mode 100644 index e876511f2..000000000 --- a/tests/TestCase.php +++ /dev/null @@ -1,120 +0,0 @@ - 'testing', - 'autowiring' => true, - 'definitions' => [], - ]; - - return ContainerFactory::create(array_merge($defaultConfig, $config)); - } - - /** - * Create a container with custom service definitions - */ - protected function createContainerWithDefinitions(array $definitions): Container - { - return $this->createTestContainer([ - 'definitions' => $definitions, - ]); - } - - /** - * Create a temporary test root directory - */ - protected function createTestRootDirectory(): string - { - $tempDir = createTempDirectory(); - - // Create basic directory structure - mkdir($tempDir . '/config', 0755, true); - mkdir($tempDir . '/internal_data/cache', 0755, true); - - return $tempDir; - } - - /** - * Create test configuration files - */ - protected function createTestConfigFiles(string $rootPath, array $configs = []): void - { - $configPath = $rootPath . '/config'; - - // Create container.php - if (isset($configs['container'])) { - file_put_contents( - $configPath . '/container.php', - ' $value) { - if (is_string($value)) { - $servicesContent .= " '$key' => factory(function () { return '$value'; }),\n"; - } - } - $servicesContent .= "];\n"; - - file_put_contents($configPath . '/services.php', $servicesContent); - } - - // Create .env file - if (isset($configs['env'])) { - $envContent = ''; - foreach ($configs['env'] as $key => $value) { - $envContent .= "$key=$value\n"; - } - file_put_contents($rootPath . '/.env', $envContent); - } - } - - /** - * Assert that a service can be resolved from the container - */ - protected function assertCanResolve(Container $container, string $serviceId): void - { - $this->assertTrue($container->has($serviceId), "Container should have service: $serviceId"); - $this->assertNotNull($container->get($serviceId), "Should be able to resolve service: $serviceId"); - } - - /** - * Assert that a service cannot be resolved from the container - */ - protected function assertCannotResolve(Container $container, string $serviceId): void - { - $this->assertFalse($container->has($serviceId), "Container should not have service: $serviceId"); - } -} diff --git a/tests/Unit/Infrastructure/DependencyInjection/BootstrapTest.php b/tests/Unit/Infrastructure/DependencyInjection/BootstrapTest.php deleted file mode 100644 index 1a0fbaf16..000000000 --- a/tests/Unit/Infrastructure/DependencyInjection/BootstrapTest.php +++ /dev/null @@ -1,190 +0,0 @@ -createTestRootDirectory(); - - $container = Bootstrap::init($rootPath); - - expect($container)->toBeInstanceOf(Container::class); - - removeTempDirectory($rootPath); - }); - - it('returns the same container on subsequent calls', function () { - $rootPath = $this->createTestRootDirectory(); - - $container1 = Bootstrap::init($rootPath); - $container2 = Bootstrap::init($rootPath); - - expect($container1)->toBe($container2); - - removeTempDirectory($rootPath); - }); - - it('registers container instance with itself', function () { - $rootPath = $this->createTestRootDirectory(); - - $container = Bootstrap::init($rootPath); - - expect($container->get(Container::class))->toBe($container); - expect($container->get('container'))->toBe($container); - - removeTempDirectory($rootPath); - }); - - it('loads environment variables from .env file', function () { - $rootPath = $this->createTestRootDirectory(); - $this->createTestConfigFiles($rootPath, [ - 'env' => [ - 'TEST_VAR' => 'test_value', - 'APP_ENV' => 'testing', - ], - ]); - - Bootstrap::init($rootPath); - - expect($_ENV['TEST_VAR'] ?? null)->toBe('test_value'); - expect($_ENV['APP_ENV'] ?? null)->toBe('testing'); - - removeTempDirectory($rootPath); - }); - - it('loads configuration from config files', function () { - $rootPath = $this->createTestRootDirectory(); - $this->createTestConfigFiles($rootPath, [ - 'container' => [ - 'environment' => 'testing', - 'autowiring' => true, - ], - 'services' => [ - 'test.service' => 'config_value', - ], - ]); - - $container = Bootstrap::init($rootPath); - - expect($container->get('test.service'))->toBe('config_value'); - - removeTempDirectory($rootPath); - }); - - it('handles missing config files gracefully', function () { - $rootPath = $this->createTestRootDirectory(); - - // Should not throw exception even without config files - $container = Bootstrap::init($rootPath); - - expect($container)->toBeInstanceOf(Container::class); - - removeTempDirectory($rootPath); - }); - }); - - describe('getContainer() method', function () { - it('returns null when not initialized', function () { - expect(Bootstrap::getContainer())->toBeNull(); - }); - - it('returns container after initialization', function () { - $rootPath = $this->createTestRootDirectory(); - - $container = Bootstrap::init($rootPath); - - expect(Bootstrap::getContainer())->toBe($container); - - removeTempDirectory($rootPath); - }); - }); - - describe('reset() method', function () { - it('clears the container instance', function () { - $rootPath = $this->createTestRootDirectory(); - - Bootstrap::init($rootPath); - expect(Bootstrap::getContainer())->not->toBeNull(); - - Bootstrap::reset(); - expect(Bootstrap::getContainer())->toBeNull(); - - removeTempDirectory($rootPath); - }); - - it('allows re-initialization after reset', function () { - $rootPath = $this->createTestRootDirectory(); - - $container1 = Bootstrap::init($rootPath); - Bootstrap::reset(); - $container2 = Bootstrap::init($rootPath); - - expect($container1)->not->toBe($container2); - expect($container2)->toBeInstanceOf(Container::class); - - removeTempDirectory($rootPath); - }); - }); - - describe('configuration loading', function () { - it('merges configuration from multiple sources', function () { - $rootPath = $this->createTestRootDirectory(); - $this->createTestConfigFiles($rootPath, [ - 'env' => [ - 'APP_ENV' => 'production', - 'APP_DEBUG' => 'false', - ], - 'container' => [ - 'autowiring' => true, - ], - 'services' => [ - 'config.service' => 'merged_config', - ], - ]); - - $container = Bootstrap::init($rootPath, [ - 'definitions' => [ - 'runtime.service' => \DI\factory(function () { - return 'runtime_config'; - }), - ], - ]); - - expect($container->get('config.service'))->toBe('merged_config'); - expect($container->get('runtime.service'))->toBe('runtime_config'); - - removeTempDirectory($rootPath); - }); - - it('sets default environment when no .env file exists', function () { - $rootPath = $this->createTestRootDirectory(); - - $container = Bootstrap::init($rootPath); - - // Container should still be created successfully - expect($container)->toBeInstanceOf(Container::class); - - removeTempDirectory($rootPath); - }); - }); - - describe('error handling', function () { - it('handles invalid root path gracefully', function () { - // Should not throw fatal error for non-existent path - expect(function () { - Bootstrap::init('/non/existent/path'); - })->not->toThrow(Throwable::class); - }); - }); -}); diff --git a/tests/Unit/Infrastructure/DependencyInjection/ContainerFactoryTest.php b/tests/Unit/Infrastructure/DependencyInjection/ContainerFactoryTest.php deleted file mode 100644 index ccd6e1ea8..000000000 --- a/tests/Unit/Infrastructure/DependencyInjection/ContainerFactoryTest.php +++ /dev/null @@ -1,206 +0,0 @@ -toBeInstanceOf(Container::class); - }); - - it('applies configuration correctly', function () { - $config = [ - 'environment' => 'testing', - 'autowiring' => true, - 'annotations' => false, - ]; - - $container = ContainerFactory::create($config); - expect($container)->toBeInstanceOf(Container::class); - }); - - it('loads custom definitions', function () { - $config = [ - 'definitions' => [ - 'test.service' => \DI\factory(function () { - return 'test_value'; - }), - ], - ]; - - $container = ContainerFactory::create($config); - expect($container->get('test.service'))->toBe('test_value'); - }); - - it('configures autowiring when enabled', function () { - $config = ['autowiring' => true]; - $container = ContainerFactory::create($config); - - // Should be able to autowire stdClass - expect($container->has(stdClass::class))->toBeTrue(); - }); - - it('loads definition files when specified', function () { - $tempDir = createTempDirectory(); - $definitionFile = $tempDir . '/definitions.php'; - - file_put_contents($definitionFile, ' \DI\factory(function () { - return "from_file"; - }), - ];'); - - $config = [ - 'definition_files' => [$definitionFile], - ]; - - $container = ContainerFactory::create($config); - expect($container->get('file.service'))->toBe('from_file'); - - removeTempDirectory($tempDir); - }); - - it('handles non-existent definition files gracefully', function () { - $config = [ - 'definition_files' => ['/non/existent/file.php'], - ]; - - // Should not throw an exception - $container = ContainerFactory::create($config); - expect($container)->toBeInstanceOf(Container::class); - }); - }); - - describe('service providers', function () { - it('registers and boots service providers', function () { - $providerClass = new class implements ServiceProvider { - public static bool $registered = false; - public static bool $booted = false; - - public function register(Container $container): void - { - self::$registered = true; - $container->getWrappedContainer()->set('provider.service', 'provider_value'); - } - - public function boot(Container $container): void - { - self::$booted = true; - } - }; - - $config = [ - 'providers' => [get_class($providerClass)], - ]; - - $container = ContainerFactory::create($config); - - expect($providerClass::$registered)->toBeTrue(); - expect($providerClass::$booted)->toBeTrue(); - expect($container->get('provider.service'))->toBe('provider_value'); - }); - - it('handles invalid provider classes gracefully', function () { - $config = [ - 'providers' => ['NonExistentProvider'], - ]; - - // Should not throw an exception - $container = ContainerFactory::create($config); - expect($container)->toBeInstanceOf(Container::class); - }); - - it('boots providers after all registrations', function () { - // Use a simpler approach without constructor dependencies - $testFile = sys_get_temp_dir() . '/provider_order_test.txt'; - if (file_exists($testFile)) { - unlink($testFile); - } - - $provider1Class = new class implements ServiceProvider { - public function register(Container $container): void - { - $testFile = sys_get_temp_dir() . '/provider_order_test.txt'; - file_put_contents($testFile, "register1\n", FILE_APPEND); - } - - public function boot(Container $container): void - { - $testFile = sys_get_temp_dir() . '/provider_order_test.txt'; - file_put_contents($testFile, "boot1\n", FILE_APPEND); - } - }; - - $provider2Class = new class implements ServiceProvider { - public function register(Container $container): void - { - $testFile = sys_get_temp_dir() . '/provider_order_test.txt'; - file_put_contents($testFile, "register2\n", FILE_APPEND); - } - - public function boot(Container $container): void - { - $testFile = sys_get_temp_dir() . '/provider_order_test.txt'; - file_put_contents($testFile, "boot2\n", FILE_APPEND); - } - }; - - $config = [ - 'providers' => [get_class($provider1Class), get_class($provider2Class)], - ]; - - ContainerFactory::create($config); - - // Read the order from the test file - $content = file_get_contents($testFile); - $lines = array_filter(explode("\n", trim($content))); - - // All registrations should happen before any boots - expect($lines)->toBe(['register1', 'register2', 'boot1', 'boot2']); - - // Clean up - unlink($testFile); - }); - }); - - describe('environment configuration', function () { - it('enables compilation in production', function () { - $tempDir = createTempDirectory(); - - $config = [ - 'environment' => 'production', - 'compilation_dir' => $tempDir . '/container', - 'proxies_dir' => $tempDir . '/proxies', - ]; - - $container = ContainerFactory::create($config); - expect($container)->toBeInstanceOf(Container::class); - - removeTempDirectory($tempDir); - }); - - it('skips compilation in development', function () { - $config = [ - 'environment' => 'development', - ]; - - $container = ContainerFactory::create($config); - expect($container)->toBeInstanceOf(Container::class); - }); - }); - - describe('layer definitions integration', function () { - it('loads definitions from all architectural layers', function () { - $container = ContainerFactory::create(); - - // Container should be created successfully with all layer definitions - expect($container)->toBeInstanceOf(Container::class); - - // Since most definitions are commented out, we just verify the container works - expect($container->has(stdClass::class))->toBeTrue(); - }); - }); -}); diff --git a/tests/Unit/Infrastructure/DependencyInjection/ContainerTest.php b/tests/Unit/Infrastructure/DependencyInjection/ContainerTest.php deleted file mode 100644 index d751ecf67..000000000 --- a/tests/Unit/Infrastructure/DependencyInjection/ContainerTest.php +++ /dev/null @@ -1,165 +0,0 @@ -container = $this->createTestContainer(); - }); - - it('implements PSR-11 ContainerInterface', function () { - expect($this->container)->toBeInstanceOf(\Psr\Container\ContainerInterface::class); - }); - - describe('get() method', function () { - it('can resolve a simple service', function () { - $container = $this->createContainerWithDefinitions([ - 'test.service' => \DI\factory(function () { - return 'test_value'; - }), - ]); - - $result = $container->get('test.service'); - expect($result)->toBe('test_value'); - }); - - it('can resolve autowired classes', function () { - $container = $this->createContainerWithDefinitions([ - 'test.class' => \DI\autowire(stdClass::class), - ]); - - $result = $container->get('test.class'); - expect($result)->toBeInstanceOf(stdClass::class); - }); - - it('throws NotFoundExceptionInterface for non-existent services', function () { - expectException( - fn() => $this->container->get('non.existent.service'), - NotFoundExceptionInterface::class, - 'non.existent.service' - ); - }); - - it('returns same instance for singleton services', function () { - $container = $this->createContainerWithDefinitions([ - 'singleton.service' => \DI\factory(function () { - return new stdClass(); - }), - ]); - - $instance1 = $container->get('singleton.service'); - $instance2 = $container->get('singleton.service'); - - expect($instance1)->toBe($instance2); - }); - }); - - describe('has() method', function () { - it('returns true for existing services', function () { - $container = $this->createContainerWithDefinitions([ - 'existing.service' => \DI\factory(function () { - return 'value'; - }), - ]); - - expect($container->has('existing.service'))->toBeTrue(); - }); - - it('returns false for non-existent services', function () { - expect($this->container->has('non.existent.service'))->toBeFalse(); - }); - - it('returns true for autowirable classes', function () { - expect($this->container->has(stdClass::class))->toBeTrue(); - }); - }); - - describe('make() method', function () { - it('can make instances with parameters', function () { - $result = $this->container->make(stdClass::class); - expect($result)->toBeInstanceOf(stdClass::class); - }); - - it('creates new instances each time', function () { - $instance1 = $this->container->make(stdClass::class); - $instance2 = $this->container->make(stdClass::class); - - expect($instance1)->not->toBe($instance2); - }); - }); - - describe('call() method', function () { - it('can call closures with dependency injection', function () { - $result = $this->container->call(function (stdClass $class) { - return get_class($class); - }); - - expect($result)->toBe('stdClass'); - }); - - it('can call methods with parameters', function () { - $service = new class { - public function test(string $param): string - { - return "Hello $param"; - } - }; - - $result = $this->container->call([$service, 'test'], ['param' => 'World']); - expect($result)->toBe('Hello World'); - }); - }); - - describe('injectOn() method', function () { - it('returns the object after injection', function () { - $object = new stdClass(); - - $result = $this->container->injectOn($object); - expect($result)->toBe($object); - }); - }); - - describe('getWrappedContainer() method', function () { - it('returns the underlying PHP-DI container', function () { - $wrapped = $this->container->getWrappedContainer(); - expect($wrapped)->toBeInstanceOf(\DI\Container::class); - }); - - it('allows direct access to PHP-DI functionality', function () { - $wrapped = $this->container->getWrappedContainer(); - $wrapped->set('direct.service', 'direct_value'); - - expect($this->container->get('direct.service'))->toBe('direct_value'); - }); - }); - - describe('error handling', function () { - it('provides meaningful error messages for missing services', function () { - expectException( - fn() => $this->container->get('missing.service'), - NotFoundExceptionInterface::class, - 'missing.service' - ); - }); - - it('handles circular dependencies gracefully', function () { - $container = $this->createContainerWithDefinitions([ - 'service.a' => \DI\factory(function (\Psr\Container\ContainerInterface $c) { - return $c->get('service.b'); - }), - 'service.b' => \DI\factory(function (\Psr\Container\ContainerInterface $c) { - return $c->get('service.a'); - }), - ]); - - expectException( - fn() => $container->get('service.a'), - ContainerExceptionInterface::class, - 'Circular dependency' - ); - }); - }); -}); diff --git a/tests/Unit/Infrastructure/DependencyInjection/Definitions/ApplicationDefinitionsTest.php b/tests/Unit/Infrastructure/DependencyInjection/Definitions/ApplicationDefinitionsTest.php deleted file mode 100644 index 8d686cea1..000000000 --- a/tests/Unit/Infrastructure/DependencyInjection/Definitions/ApplicationDefinitionsTest.php +++ /dev/null @@ -1,100 +0,0 @@ -toBeArray(); - }); - - it('returns empty array when no application services are implemented yet', function () { - $definitions = ApplicationDefinitions::getDefinitions(); - - // Since we're in Phase 1 and application services aren't implemented yet, - // the definitions should be empty (all examples are commented out) - expect($definitions)->toBe([]); - }); - - it('follows application layer principles', function () { - // Application layer should orchestrate domain objects - // This test verifies the structure is ready for future application services - - $definitions = ApplicationDefinitions::getDefinitions(); - - // Should be an array (even if empty) - expect($definitions)->toBeArray(); - - // When application services are added, they should follow these principles: - // - Command and Query handlers - // - Application services that orchestrate domain logic - // - Event dispatchers - // - No direct infrastructure concerns - }); - - it('can be safely called multiple times', function () { - $definitions1 = ApplicationDefinitions::getDefinitions(); - $definitions2 = ApplicationDefinitions::getDefinitions(); - - expect($definitions1)->toBe($definitions2); - }); - - it('is prepared for future command/query handlers', function () { - // This test documents the intended structure for Phase 3 implementation - - $definitions = ApplicationDefinitions::getDefinitions(); - expect($definitions)->toBeArray(); - - // Future command/query handlers will be registered like: - // 'TorrentPier\Application\User\Handler\RegisterUserHandler' => DI\autowire(), - // 'CommandBusInterface' => DI\factory(function (ContainerInterface $c) { - // return new CommandBus($c); - // }), - - // For now, verify the method works without breaking - expect(count($definitions))->toBeGreaterThanOrEqual(0); - }); - }); - - describe('architectural compliance', function () { - it('follows hexagonal architecture principles', function () { - // Application layer should orchestrate domain objects without infrastructure concerns - - $definitions = ApplicationDefinitions::getDefinitions(); - - // Application definitions should focus on: - // 1. Command and Query handlers - // 2. Application services - // 3. Event dispatchers - // 4. Use case orchestration - - expect($definitions)->toBeArray(); - }); - - it('supports CQRS pattern', function () { - // Application layer should separate commands and queries - // This test ensures the structure supports CQRS implementation - - $definitions = ApplicationDefinitions::getDefinitions(); - - // Future implementation will separate: - // - Command handlers (write operations) - // - Query handlers (read operations) - // - Command and Query buses - - expect($definitions)->toBeArray(); - }); - - it('prepares for event-driven architecture', function () { - // Application layer should support domain events - - $definitions = ApplicationDefinitions::getDefinitions(); - - // Future event dispatcher will be registered here - // 'EventDispatcherInterface' => DI\factory(...) - - expect($definitions)->toBeArray(); - }); - }); -}); diff --git a/tests/Unit/Infrastructure/DependencyInjection/Definitions/DomainDefinitionsTest.php b/tests/Unit/Infrastructure/DependencyInjection/Definitions/DomainDefinitionsTest.php deleted file mode 100644 index 787083024..000000000 --- a/tests/Unit/Infrastructure/DependencyInjection/Definitions/DomainDefinitionsTest.php +++ /dev/null @@ -1,84 +0,0 @@ -toBeArray(); - }); - - it('returns empty array when no domain services are implemented yet', function () { - $definitions = DomainDefinitions::getDefinitions(); - - // Since we're in Phase 1 and domain services aren't implemented yet, - // the definitions should be empty (all examples are commented out) - expect($definitions)->toBe([]); - }); - - it('follows domain layer principles', function () { - // Domain definitions should not contain infrastructure dependencies - // This test verifies the structure is ready for future domain services - - $definitions = DomainDefinitions::getDefinitions(); - - // Should be an array (even if empty) - expect($definitions)->toBeArray(); - - // When domain services are added, they should follow these principles: - // - No framework dependencies - // - Repository interfaces mapped to implementations - // - Pure business logic services - }); - - it('can be safely called multiple times', function () { - $definitions1 = DomainDefinitions::getDefinitions(); - $definitions2 = DomainDefinitions::getDefinitions(); - - expect($definitions1)->toBe($definitions2); - }); - - it('is prepared for future repository interface mappings', function () { - // This test documents the intended structure for Phase 2 implementation - - $definitions = DomainDefinitions::getDefinitions(); - expect($definitions)->toBeArray(); - - // Future repository interfaces will be mapped like: - // 'TorrentPier\Domain\User\Repository\UserRepositoryInterface' => - // DI\factory(function (ContainerInterface $c) { - // return $c->get('TorrentPier\Infrastructure\Persistence\Repository\UserRepository'); - // }), - - // For now, verify the method works without breaking - expect(count($definitions))->toBeGreaterThanOrEqual(0); - }); - }); - - describe('architectural compliance', function () { - it('follows hexagonal architecture principles', function () { - // Domain layer should have no infrastructure dependencies - // This test ensures the definition structure is correct - - $definitions = DomainDefinitions::getDefinitions(); - - // Domain definitions should focus on: - // 1. Repository interface mappings - // 2. Domain service factories - // 3. No framework dependencies - - expect($definitions)->toBeArray(); - }); - - it('supports dependency injection inversion', function () { - // Domain interfaces should be mapped to infrastructure implementations - // following the dependency inversion principle - - $definitions = DomainDefinitions::getDefinitions(); - - // Even though empty now, the structure supports proper DI mapping - expect($definitions)->toBeArray(); - }); - }); -}); diff --git a/tests/Unit/Infrastructure/DependencyInjection/Definitions/InfrastructureDefinitionsTest.php b/tests/Unit/Infrastructure/DependencyInjection/Definitions/InfrastructureDefinitionsTest.php deleted file mode 100644 index 64ba55cee..000000000 --- a/tests/Unit/Infrastructure/DependencyInjection/Definitions/InfrastructureDefinitionsTest.php +++ /dev/null @@ -1,159 +0,0 @@ -toBeArray(); - }); - - it('accepts configuration parameter', function () { - $config = ['test' => 'value']; - $definitions = InfrastructureDefinitions::getDefinitions($config); - expect($definitions)->toBeArray(); - }); - - it('returns empty array when no infrastructure services are implemented yet', function () { - $definitions = InfrastructureDefinitions::getDefinitions(); - - // Since we're in Phase 1 and infrastructure services aren't implemented yet, - // the definitions should be empty (all examples are commented out) - expect($definitions)->toBe([]); - }); - - it('follows infrastructure layer principles', function () { - // Infrastructure layer should handle external concerns - // This test verifies the structure is ready for future infrastructure services - - $definitions = InfrastructureDefinitions::getDefinitions(); - - // Should be an array (even if empty) - expect($definitions)->toBeArray(); - - // When infrastructure services are added, they should follow these principles: - // - Database connections and repositories - // - Cache implementations - // - External service adapters - // - File storage systems - }); - - it('can be safely called multiple times', function () { - $definitions1 = InfrastructureDefinitions::getDefinitions(); - $definitions2 = InfrastructureDefinitions::getDefinitions(); - - expect($definitions1)->toBe($definitions2); - }); - - it('can handle different configurations', function () { - $config1 = ['database' => ['host' => 'localhost']]; - $config2 = ['cache' => ['driver' => 'redis']]; - - $definitions1 = InfrastructureDefinitions::getDefinitions($config1); - $definitions2 = InfrastructureDefinitions::getDefinitions($config2); - - // Should handle different configs without breaking - expect($definitions1)->toBeArray(); - expect($definitions2)->toBeArray(); - }); - - it('is prepared for future database services', function () { - // This test documents the intended structure for Phase 4 implementation - - $definitions = InfrastructureDefinitions::getDefinitions([ - 'database' => [ - 'host' => '127.0.0.1', - 'port' => 3306, - 'database' => 'tp', - 'username' => 'root', - 'password' => '', - 'charset' => 'utf8mb4', - ], - ]); - - expect($definitions)->toBeArray(); - - // Future database services will be registered like: - // 'database.connection.default' => DI\factory(function () use ($config) { ... }), - // Connection::class => DI\get('database.connection.default'), - - // For now, verify the method works without breaking - expect(count($definitions))->toBeGreaterThanOrEqual(0); - }); - - it('is prepared for future cache services', function () { - $definitions = InfrastructureDefinitions::getDefinitions([ - 'cache' => [ - 'driver' => 'file', - 'file' => ['path' => '/tmp/cache'], - ], - ]); - - expect($definitions)->toBeArray(); - - // Future cache services will be registered like: - // 'cache.storage' => DI\factory(function () use ($config) { ... }), - // 'cache.factory' => DI\factory(function (ContainerInterface $c) { ... }), - }); - }); - - describe('architectural compliance', function () { - it('follows hexagonal architecture principles', function () { - // Infrastructure layer should handle external concerns and adapters - - $definitions = InfrastructureDefinitions::getDefinitions(); - - // Infrastructure definitions should focus on: - // 1. Database connections and persistence - // 2. Cache implementations - // 3. External service adapters - // 4. File storage systems - // 5. Third-party integrations - - expect($definitions)->toBeArray(); - }); - - it('supports dependency inversion', function () { - // Infrastructure should implement domain interfaces - - $definitions = InfrastructureDefinitions::getDefinitions(); - - // Future repository implementations will be registered here: - // 'TorrentPier\Infrastructure\Persistence\Repository\UserRepository' => DI\autowire() - // ->constructorParameter('connection', DI\get('database.connection.default')) - - expect($definitions)->toBeArray(); - }); - - it('handles configuration-based service creation', function () { - // Infrastructure services should be configurable - - $config = [ - 'database' => ['driver' => 'mysql'], - 'cache' => ['driver' => 'redis'], - 'storage' => ['driver' => 's3'], - ]; - - $definitions = InfrastructureDefinitions::getDefinitions($config); - - // Should handle configuration without breaking - expect($definitions)->toBeArray(); - }); - - it('prepares for multiple database connections', function () { - $config = [ - 'database' => [ - 'default' => 'mysql', - 'connections' => [ - 'mysql' => ['driver' => 'mysql'], - 'sqlite' => ['driver' => 'sqlite'], - ], - ], - ]; - - $definitions = InfrastructureDefinitions::getDefinitions($config); - expect($definitions)->toBeArray(); - }); - }); -}); diff --git a/tests/Unit/Infrastructure/DependencyInjection/Definitions/PresentationDefinitionsTest.php b/tests/Unit/Infrastructure/DependencyInjection/Definitions/PresentationDefinitionsTest.php deleted file mode 100644 index b751b70ba..000000000 --- a/tests/Unit/Infrastructure/DependencyInjection/Definitions/PresentationDefinitionsTest.php +++ /dev/null @@ -1,147 +0,0 @@ -toBeArray(); - }); - - it('returns empty array when no presentation services are implemented yet', function () { - $definitions = PresentationDefinitions::getDefinitions(); - - // Since we're in Phase 1 and presentation services aren't implemented yet, - // the definitions should be empty (all examples are commented out) - expect($definitions)->toBe([]); - }); - - it('follows presentation layer principles', function () { - // Presentation layer should handle user interface concerns - // This test verifies the structure is ready for future presentation services - - $definitions = PresentationDefinitions::getDefinitions(); - - // Should be an array (even if empty) - expect($definitions)->toBeArray(); - - // When presentation services are added, they should follow these principles: - // - HTTP controllers for web and API interfaces - // - CLI commands for console operations - // - Middleware for request/response processing - // - Response transformers for output formatting - }); - - it('can be safely called multiple times', function () { - $definitions1 = PresentationDefinitions::getDefinitions(); - $definitions2 = PresentationDefinitions::getDefinitions(); - - expect($definitions1)->toBe($definitions2); - }); - - it('is prepared for future HTTP controllers', function () { - // This test documents the intended structure for Phase 5 implementation - - $definitions = PresentationDefinitions::getDefinitions(); - expect($definitions)->toBeArray(); - - // Future HTTP controllers will be registered like: - // 'TorrentPier\Presentation\Http\Controllers\Web\HomeController' => DI\autowire(), - // 'TorrentPier\Presentation\Http\Controllers\Api\UserController' => DI\autowire(), - // 'TorrentPier\Presentation\Http\Controllers\Admin\DashboardController' => DI\autowire(), - - // For now, verify the method works without breaking - expect(count($definitions))->toBeGreaterThanOrEqual(0); - }); - - it('is prepared for future CLI commands', function () { - $definitions = PresentationDefinitions::getDefinitions(); - expect($definitions)->toBeArray(); - - // Future CLI commands will be registered like: - // 'TorrentPier\Presentation\Cli\Commands\CacheCommand' => DI\autowire(), - // 'TorrentPier\Presentation\Cli\Commands\MigrateCommand' => DI\autowire(), - }); - - it('is prepared for future middleware', function () { - $definitions = PresentationDefinitions::getDefinitions(); - expect($definitions)->toBeArray(); - - // Future middleware will be registered like: - // 'AuthenticationMiddleware' => DI\autowire('TorrentPier\Presentation\Http\Middleware\AuthenticationMiddleware'), - // 'CorsMiddleware' => DI\autowire('TorrentPier\Presentation\Http\Middleware\CorsMiddleware'), - }); - }); - - describe('architectural compliance', function () { - it('follows hexagonal architecture principles', function () { - // Presentation layer should handle user interface and external interfaces - - $definitions = PresentationDefinitions::getDefinitions(); - - // Presentation definitions should focus on: - // 1. HTTP controllers (Web, API, Admin) - // 2. CLI commands - // 3. Middleware for request processing - // 4. Response transformers - // 5. Input validation and output formatting - - expect($definitions)->toBeArray(); - }); - - it('supports multiple interface types', function () { - // Presentation layer should support web, API, and CLI interfaces - - $definitions = PresentationDefinitions::getDefinitions(); - - // Future implementation will include: - // - Web controllers for HTML responses - // - API controllers for JSON responses - // - Admin controllers for administrative interface - // - CLI commands for console operations - - expect($definitions)->toBeArray(); - }); - - it('prepares for middleware stack', function () { - // Presentation layer should support request/response middleware - - $definitions = PresentationDefinitions::getDefinitions(); - - // Future middleware will handle: - // - Authentication and authorization - // - CORS headers - // - Rate limiting - // - Request validation - // - Response transformation - - expect($definitions)->toBeArray(); - }); - - it('supports dependency injection for controllers', function () { - // Controllers should have their dependencies injected - - $definitions = PresentationDefinitions::getDefinitions(); - - // Future controllers will be autowired with dependencies: - // - Application services (command/query handlers) - // - Request validators - // - Response transformers - - expect($definitions)->toBeArray(); - }); - - it('prepares for different response formats', function () { - // Presentation layer should support multiple response formats - - $definitions = PresentationDefinitions::getDefinitions(); - - // Future response transformers: - // 'JsonResponseTransformer' => DI\autowire(...), - // 'HtmlResponseTransformer' => DI\autowire(...), - - expect($definitions)->toBeArray(); - }); - }); -}); diff --git a/tests/Unit/Infrastructure/DependencyInjection/ServiceProviderTest.php b/tests/Unit/Infrastructure/DependencyInjection/ServiceProviderTest.php deleted file mode 100644 index 69c7c68ac..000000000 --- a/tests/Unit/Infrastructure/DependencyInjection/ServiceProviderTest.php +++ /dev/null @@ -1,144 +0,0 @@ -isInterface())->toBeTrue(); - expect($reflection->hasMethod('register'))->toBeTrue(); - expect($reflection->hasMethod('boot'))->toBeTrue(); - }); - - it('register method has correct signature', function () { - $reflection = new ReflectionClass(ServiceProvider::class); - $method = $reflection->getMethod('register'); - - expect($method->isPublic())->toBeTrue(); - expect($method->getParameters())->toHaveCount(1); - expect($method->getParameters()[0]->getType()?->getName())->toBe(Container::class); - expect($method->getReturnType()?->getName())->toBe('void'); - }); - - it('boot method has correct signature', function () { - $reflection = new ReflectionClass(ServiceProvider::class); - $method = $reflection->getMethod('boot'); - - expect($method->isPublic())->toBeTrue(); - expect($method->getParameters())->toHaveCount(1); - expect($method->getParameters()[0]->getType()?->getName())->toBe(Container::class); - expect($method->getReturnType()?->getName())->toBe('void'); - }); -}); - -describe('ServiceProvider implementation examples', function () { - it('can implement a basic service provider', function () { - $provider = new class implements ServiceProvider { - public function register(Container $container): void - { - $container->getWrappedContainer()->set('example.service', 'registered'); - } - - public function boot(Container $container): void - { - // Boot logic here - } - }; - - $container = $this->createTestContainer(); - - $provider->register($container); - - expect($container->get('example.service'))->toBe('registered'); - }); - - it('can implement a provider with complex services', function () { - $provider = new class implements ServiceProvider { - public function register(Container $container): void - { - $container->getWrappedContainer()->set('complex.service', \DI\factory(function () { - return new class { - public function getValue(): string - { - return 'complex_value'; - } - }; - })); - } - - public function boot(Container $container): void - { - // Could perform additional setup here - $service = $container->get('complex.service'); - // Setup complete - } - }; - - $container = $this->createTestContainer(); - - $provider->register($container); - $provider->boot($container); - - $service = $container->get('complex.service'); - expect($service->getValue())->toBe('complex_value'); - }); - - it('can implement a provider that registers multiple services', function () { - $provider = new class implements ServiceProvider { - public function register(Container $container): void - { - $wrapped = $container->getWrappedContainer(); - - $wrapped->set('service.a', 'value_a'); - $wrapped->set('service.b', 'value_b'); - $wrapped->set('service.c', \DI\factory(function () { - return 'value_c'; - })); - } - - public function boot(Container $container): void - { - // Boot all registered services - } - }; - - $container = $this->createTestContainer(); - - $provider->register($container); - $provider->boot($container); - - expect($container->get('service.a'))->toBe('value_a'); - expect($container->get('service.b'))->toBe('value_b'); - expect($container->get('service.c'))->toBe('value_c'); - }); - - it('boot method can access services registered by register method', function () { - $bootedServices = []; - - $provider = new class($bootedServices) implements ServiceProvider { - public function __construct(private array &$bootedServices) - { - } - - public function register(Container $container): void - { - $container->getWrappedContainer()->set('bootable.service', 'registered_value'); - } - - public function boot(Container $container): void - { - $value = $container->get('bootable.service'); - $this->bootedServices[] = $value; - } - }; - - $container = $this->createTestContainer(); - - $provider->register($container); - $provider->boot($container); - - expect($bootedServices)->toBe(['registered_value']); - }); -});