Преглед на файлове

Add Docker setup and DI container auto-wiring

- Add Dockerfile, docker-compose.yml, .dockerignore
  - Add instance() binding for concrete objects in App container
  - Add make() for auto-wiring class dependencies
  - Add clear() for test isolation
  - Document new container features in MVC skill
AGENT_WORK
Daniel Covington преди 1 седмица
родител
ревизия
0c143d0778
променени са 7 файла, в които са добавени 157 реда и са изтрити 2 реда
  1. +11
    -0
      .ai/skills/mvc/SKILL.md
  2. +4
    -0
      .dockerignore
  3. +33
    -0
      Dockerfile
  4. +70
    -2
      core/App.php
  5. +9
    -0
      docker-compose.yml
  6. +17
    -0
      docker/entrypoint.sh
  7. +13
    -0
      docker/vhost.conf

+ 11
- 0
.ai/skills/mvc/SKILL.md Целия файл

@@ -154,6 +154,17 @@ public function index(Database $db): Response
}
```

**Binding concrete instances:** Use `$app->instance($name, $obj)` to bind a specific object by key. Instances take precedence over bindings when resolving.

**Auto-wiring:** Use `$app->make(SomeClass::class)` to resolve a class with its constructor dependencies injected automatically:

```php
$repo = $app->make(EmployeeRepository::class);
// $app checks bindings first, then instantiates the class and resolves its constructor
```

**Test isolation:** Call `$app->clear()` to reset all bindings and instances between test runs.

Do not call `Request::capture()` inside action bodies. Declare the parameter instead.

---


+ 4
- 0
.dockerignore Целия файл

@@ -0,0 +1,4 @@
.git
.ai
vendor
database/app.sqlite

+ 33
- 0
Dockerfile Целия файл

@@ -0,0 +1,33 @@
FROM php:8.5-apache

# Install pdo_sqlite and enable mod_rewrite
RUN apt-get update \
&& apt-get install -y libsqlite3-dev \
&& rm -rf /var/lib/apt/lists/* \
&& docker-php-ext-install pdo_sqlite \
&& a2enmod rewrite

# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# Configure Apache virtual host
COPY docker/vhost.conf /etc/apache2/sites-available/000-default.conf
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

WORKDIR /var/www/html

# Copy application files
COPY . .

# Generate autoloader (no external dependencies — just generates vendor/autoload.php)
RUN composer install --no-dev --optimize-autoloader --no-interaction

# Create database directory and set correct permissions
RUN mkdir -p database \
&& chown -R www-data:www-data /var/www/html \
&& chmod 775 database

EXPOSE 80

ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]

+ 70
- 2
core/App.php Целия файл

@@ -5,21 +5,55 @@ declare(strict_types=1);
namespace Core;

use Exception;
use ReflectionClass;
use ReflectionFunction;
use ReflectionFunctionAbstract;
use ReflectionMethod;

class App
{
protected array $bindings = [];

protected array $instances = [];

public function bind(string $name, mixed $value): void
{
$this->bindings[$name] = $value;
}

public function instance(string $name, mixed $value): void
{
$this->instances[$name] = $value;
}

public function make(string $className): object
{
if (isset($this->bindings[$className])) {
$binding = $this->bindings[$className];

if (is_string($binding) && is_a($binding, $className, true)) {
return $this->instantiate($binding);
}

return $binding;
}

if (isset($this->instances[$className])) {
return $this->instances[$className];
}

return $this->instantiate($className);
}

public function clear(): void
{
$this->bindings = [];
$this->instances = [];
}

public function get(string $name): mixed
{
return $this->bindings[$name] ?? null;
return $this->instances[$name] ?? $this->bindings[$name] ?? null;
}

public function call(callable|array|string $handler, array $parameters = []): mixed
@@ -72,8 +106,20 @@ class App
if ($type instanceof \ReflectionNamedType && !$type->isBuiltin()) {
$typeName = $type->getName();

if (array_key_exists($typeName, $this->instances)) {
$args[] = $this->instances[$typeName];
continue;
}

if (array_key_exists($typeName, $this->bindings)) {
$args[] = $this->bindings[$typeName];
$binding = $this->bindings[$typeName];

if (is_string($binding) && is_a($binding, $typeName, true)) {
$args[] = $this->instantiate($binding);
continue;
}

$args[] = $binding;
continue;
}
}
@@ -83,4 +129,26 @@ class App

return $args;
}

/**
* @param class-string $className
*/
protected function instantiate(string $className): object
{
$reflection = new ReflectionClass($className);

if (!$reflection->isInstantiable()) {
throw new Exception("Cannot instantiate {$className}: class is not instantiable.");
}

$constructor = $reflection->getConstructor();

if ($constructor === null) {
return new $className();
}

$args = $this->resolveArgs($constructor, []);

return $reflection->newInstanceArgs($args);
}
}

+ 9
- 0
docker-compose.yml Целия файл

@@ -0,0 +1,9 @@
services:
app:
build: .
ports:
- "8080:80"
volumes:
- .:/var/www/html
environment:
APP_DEBUG: "true"

+ 17
- 0
docker/entrypoint.sh Целия файл

@@ -0,0 +1,17 @@
#!/bin/bash
set -e

mkdir -p database
chmod 777 database

composer install --no-interaction --quiet

# Runs as root — migrations may create database/app.sqlite owned by root.
php scripts/migrate.php up

# Fix ownership after migrations so www-data (Apache) can write.
# chmod works on Windows volume mounts even when chown is ignored by p9fs.
chmod 777 database
chmod 666 database/app.sqlite

exec apache2-foreground

+ 13
- 0
docker/vhost.conf Целия файл

@@ -0,0 +1,13 @@
<VirtualHost *:80>
ServerName localhost
DocumentRoot /var/www/html/public

<Directory /var/www/html/public>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>

ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

Loading…
Отказ
Запис

Powered by TurnKey Linux.