Metadata-Version: 2.1
Name: wexample-wex-addon-dev-php
Version: 11.0.0
Summary: Extends wex with PHP-specific development commands for Composer, Laravel, Symfony, and WordPress services running in Docker containers
Author-Email: weeger <contact@wexample.com>
License: MIT
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Project-URL: homepage, https://github.com/wexample/python-wex-dev-python
Requires-Python: >=3.10
Requires-Dist: attrs>=23.1.0
Requires-Dist: cattrs>=23.1.0
Requires-Dist: wexample-filestate-php>=6.4.0
Requires-Dist: wexample-wex-addon-ai>=13.0.0
Requires-Dist: wexample-wex-addon-app>=30.0.0
Requires-Dist: wexample-wex-addon-dev-javascript>=8.1.0
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Description-Content-Type: text/markdown

# wex_addon_dev_php

Version: 11.0.0

`wex_addon_dev_php` extends the wex CLI with PHP-specific development commands for Composer, Laravel, Symfony, and WordPress services running in Docker containers. It is aimed at PHP developers who manage their apps through wex and need framework-aware automation — dependency wiring, migration runs, lock-file refreshes — executed directly inside the running service containers. The addon registers four workdir types (`php`, `php-package`, `php-packages-suite`, `php-symfony`) and contributes its commands through the standard `PhpAddonManager` plugin point.

## Table of Contents

- [Installation](#installation)
- [Quickstart](#quickstart)
- [Tests](#tests)
- [Architecture](#architecture)
- [Integration in the Suite](#integration-in-the-suite)
- [Dependencies](#dependencies)
- [Versioning & Compatibility Policy](#versioning--compatibility-policy)
- [License](#license)
- [About us](#about-us)
- [Known Limitations & Roadmap](#known-limitations--roadmap)
- [Status & Compatibility](#status--compatibility)
- [Useful Links](#useful-links)
- [Migration Notes](#migration-notes)

## Installation

```bash
pip install wexample-wex-addon-dev-php
```

Requires Python >=3.10.

## Quickstart

Install the package:

```bash
pip install wexample-wex-addon-dev-php
```

The addon's plugin class is in src/wexample_wex_addon_dev_php/php_addon_manager.py:

```python
from wexample_wex_addon_dev_php.php_addon_manager import PhpAddonManager
```

Passing `PhpAddonManager` to the wex kernel registers four workdir types — `php`, `php-package`, `php-packages-suite`, `php-symfony` — and auto-discovers every service command under `services/`.

One ready-to-use command is `symfony/service/deploy`, defined in src/wexample_wex_addon_dev_php/services/symfony/commands/service/deploy.py. When wex invokes it against a running Symfony container it executes:

```bash
cd /var/www/html
if [ -d migrations ]; then
  bin/console doctrine:migrations:migrate --no-interaction --allow-no-migration
fi
```

Pending Doctrine migrations are applied; if there are none, the command exits cleanly.

## Tests

This project uses `pytest` for testing and `pytest-cov` for code coverage analysis.

### Installation

First, install the required testing dependencies:
```bash
.venv/bin/python -m pip install pytest pytest-cov
```

### Basic Usage

Run all tests with coverage:
```bash
.venv/bin/python -m pytest --cov --cov-report=html
```

### Common Commands
```bash
# Run tests with coverage for a specific module
.venv/bin/python -m pytest --cov=your_module

# Show which lines are not covered
.venv/bin/python -m pytest --cov=your_module --cov-report=term-missing

# Generate an HTML coverage report
.venv/bin/python -m pytest --cov=your_module --cov-report=html

# Combine terminal and HTML reports
.venv/bin/python -m pytest --cov=your_module --cov-report=term-missing --cov-report=html

# Run specific test file with coverage
.venv/bin/python -m pytest tests/test_file.py --cov=your_module --cov-report=term-missing
```

### Viewing HTML Reports

After generating an HTML report, open `htmlcov/index.html` in your browser to view detailed line-by-line coverage information.

### Coverage Threshold

To enforce a minimum coverage percentage:
```bash
.venv/bin/python -m pytest --cov=your_module --cov-fail-under=80
```

This will cause the test suite to fail if coverage drops below 80%.

## Architecture

The addon is a Python package (`wexample-wex-addon-dev-php`) that plugs into the wex CLI through a single manager class. Its parts are: the entry-point manager, a workdir layer (four types), a set of support types, and a service layer where each PHP framework contributes Docker container definitions and commands.

### Entry point

src/wexample_wex_addon_dev_php/php_addon_manager.py is the class the host kernel receives. It extends `AbstractAddonManager` and does two things: it reports the package module so wex can auto-discover every service command nested under `services/`, and it declares the four workdir types the addon adds:

```python
return {
    "php": PhpWorkdir,
    "php-package": PhpPackageWorkdir,
    "php-packages-suite": PhpPackagesSuiteWorkdir,
    "php-symfony": PhpSymfonyWorkdir,
}
```

### Workdir layer

The four workdir types form a hierarchy rooted in `PhpWorkdir`.

#### PhpWorkdir

src/wexample_wex_addon_dev_php/workdir/php_workdir.py is the base for all PHP projects. It extends `CodeBaseWorkdir` and `WithAiWorkdirMixin`. Its `prepare_value` declares the expected filesystem shape: `composer.json` (must exist), `src/` and `tests/` directories (both required), and an optional `package.json` entry that is included only when the file exists on disk. PHP source files in both directories automatically pick up `PhpcsFixerOption`. `get_app_config_file` returns a `PhpComposerJsonFile`; `get_dependency_manifests` appends `package.json` when present so that polyglot apps (Symfony or Laravel with a Node-built frontend) follow composer version syncs. Tests are run via `vendor/bin/phpunit`; `has_tests` checks for at least one `*Test.php` file under `tests/`.

#### PhpPackageWorkdir

src/wexample_wex_addon_dev_php/workdir/php_package_workdir.py extends `PhpWorkdir` and adds the mechanics for an individual distributable Composer library.

Backward-compatibility checking uses a dedicated Docker runner built from src/wexample_wex_addon_dev_php/resources/docker/Dockerfile.roave (PHP 8.4 with `roave/backward-compatibility-check` installed globally). When classifying a version bump, the workdir mounts the git repository root at `/var/www/html` and runs `roave-backward-compatibility-check --from=<last_tag>` inside the container; a non-zero exit promotes the bump to `major`.

Publishing adds a Packagist-friendly annotated tag (`vX.Y.Z`) on top of what the base class produces, then pushes to the deployment remote via `push_to_deployment_remote`.

`search_in_codebase` walks every `PhpFile` child recursively; `search_imports_in_codebase` wraps it with a regex that matches `use Vendor\Package\...;` and qualified references.

#### PhpPackagesSuiteWorkdir

src/wexample_wex_addon_dev_php/workdir/php_packages_suite_workdir.py extends `FrameworkPackageSuiteWorkdir`. It identifies child package directories by the presence of a `composer.json` file (`_child_is_package_directory`) and materialises each as a `PhpPackageWorkdir`. The child package directory name reported to the framework is `"composer"`.

#### PhpSymfonyWorkdir

src/wexample_wex_addon_dev_php/workdir/php_symfony_workdir.py extends `PhpWorkdir`. Its `apply` runs the PHP pass first, then creates a `JavascriptWorkdir` for the same path and runs its apply pass — covering Symfony apps that ship a Node-built frontend alongside their PHP source.

### Support types

#### PhpComposerJsonFile

src/wexample_wex_addon_dev_php/file/php_composer_json_file.py wraps `composer.json` as a typed `JsonFile` via `AppDependenciesConfigFileMixin`. Its responsibilities:

- `get_dependencies_versions` reads the `require` block and returns it as `dict[str, str]`
- `add_dependency_from_string` adds or updates a constraint in `require` or `require-dev`
- `dumps` injects the package name (derived from the workdir's vendor and project names) and version at write time
- `set_private_registry_packages` pins each forge-style composer repository (URL containing `/packages/composer`) to an `only` list of private package names, preventing it from masking newer Packagist versions

#### PhpPackageReadmeContentConfigValue

src/wexample_wex_addon_dev_php/config_value/php_package_readme_config_value.py extends `AppReadmeConfigValue` and overrides `_get_app_description` to pull the description field from `composer.json` before falling back to the generic base logic.

#### DomainTag

src/wexample_wex_addon_dev_php/const/tags.py declares the five domain tags the addon's commands use: `APP_LIFECYCLE`, `CONFIG`, `DB`, `FRAMEWORK`, `LANGUAGE_PHP`. Every `@command` decorator in this package references one or more of them in its `tags=` list.

### Service layer

Services live under `src/wexample_wex_addon_dev_php/services/`. Each service has a `service.yml`, a `docker/docker-compose.yml`, optional env-specific compose overlays under `samples/env/{dev,prod,local}/`, and a `commands/` sub-tree with Python or YAML command files.

#### php

Defined in src/wexample_wex_addon_dev_php/services/php/service.yml and src/wexample_wex_addon_dev_php/services/php/docker/docker-compose.yml. The base PHP service: runs `dunglas/frankenphp:php8.4`, mounts the app at `/var/www/html`, and binds a `Caddyfile` and a `web.ini` from the runtime config. The `laravel` and `symfony` containers extend this compose service definition. Sample files for the Caddyfile (serving from `public/`, `php_server` directive) and `web.ini` (upload limits, memory limit) are shipped in `services/php/samples/`.

#### frankenphp

Defined in src/wexample_wex_addon_dev_php/services/frankenphp/service.yml. A standalone FrankenPHP service with the same image and binding conventions as `php`, used when the app does not need the `php` base service name.

#### composer

Defined in src/wexample_wex_addon_dev_php/services/composer/service.yml. A dedicated Composer container (`composer:latest`) that mounts the app at `/var/www/html` and stays alive via `tail -f /dev/null`. Its commands are the generic lock-management routines that Laravel and Symfony both call.

#### laravel

Defined in src/wexample_wex_addon_dev_php/services/laravel/service.yml; extends the `php` compose service and declares a MySQL dependency. src/wexample_wex_addon_dev_php/services/laravel/app_service.py contributes a workdir filestate requirement: uid `82:82` (www-data in Alpine images) on `storage/` and `bootstrap/cache/`, enforced by `get_workdir_contribution`.

#### symfony

Defined in src/wexample_wex_addon_dev_php/services/symfony/service.yml; extends the `php` compose service. src/wexample_wex_addon_dev_php/services/symfony/app_service.py applies the same uid `82:82` requirement to `var/`. src/wexample_wex_addon_dev_php/services/symfony/knowledge/entity.md is a static knowledge fragment injected into AI context: it states that every Symfony entity must extend `AbstractEntity` from `wexample-symfony-helpers`.

#### wordpress

Defined in src/wexample_wex_addon_dev_php/services/wordpress/service.yml. Runs `wordpress:6.3.1-php8.2-apache` plus a `wordpress_cli` sidecar (`wordpress:cli-2.7.1-php8.2`) defined in src/wexample_wex_addon_dev_php/services/wordpress/docker/docker-compose.yml. WordPress commands target the sidecar by constructing its container name as `{app_project_name}_wordpress_cli`.

#### phpmyadmin

Defined in src/wexample_wex_addon_dev_php/services/phpmyadmin/service.yml. Runs `phpmyadmin/phpmyadmin:5.2` on the shared `wex_net` network; the local env overlay injects `PMA_USER` and `PMA_PASSWORD`.

### Commands

#### Composer (generic, reused by Laravel and Symfony)

src/wexample_wex_addon_dev_php/services/composer/commands/service/install_local.py is the shared local-wiring routine. It reads `local_packages.php` from the runtime config, runs `composer install --no-scripts` inside the target container, symlinks every local-dev package directory from `VENDOR_DEV_DIR/<vendor>/<pkg>` into `vendor/`, then runs `composer dump-autoload`. The constants `APP_DIR = "/var/www/html"` and `VENDOR_DEV_DIR = "/var/www/vendor-dev"` are exported so framework services can import them.

src/wexample_wex_addon_dev_php/services/composer/commands/service/refresh_lock.py runs `composer update --no-install --with-all-dependencies <packages>` inside the container. `--no-install` rewrites `composer.lock` without touching `vendor/` (which may hold local development symlinks); `--with-all-dependencies` allows sibling library constraints to move.

#### Laravel

src/wexample_wex_addon_dev_php/services/laravel/commands/service/install_local.py clears `bootstrap/cache/packages.php` and `bootstrap/cache/services.php` before delegating to `composer__service__install_local`, then runs `php artisan optimize:clear` to flush the rebuilt service-provider cache.

src/wexample_wex_addon_dev_php/services/laravel/commands/service/refresh_lock.py delegates to the generic composer and node `refresh_lock` functions, selecting each by whether the corresponding packages argument is non-empty.

src/wexample_wex_addon_dev_php/services/laravel/commands/maintenance/enable.yml is a YAML command that attaches `after app::maintenance/enable` and runs `php artisan down` in the Laravel container. Its mirror, src/wexample_wex_addon_dev_php/services/laravel/commands/maintenance/disable.yml, attaches `after app::maintenance/disable` and runs `php artisan up`.

src/wexample_wex_addon_dev_php/services/laravel/commands/db/migrate.yml attaches `before app::maintenance/disable` and runs `php artisan migrate --force --no-interaction -vvv`, so migrations complete before the app is brought back up.

#### Symfony

src/wexample_wex_addon_dev_php/services/symfony/commands/service/install_local.py calls `composer__service__install_local` and `node__service__install_local` in sequence, then runs `bin/console cache:clear` inside the container.

src/wexample_wex_addon_dev_php/services/symfony/commands/service/deploy.py is the post-deployment hook: it runs `doctrine:migrations:migrate --no-interaction --allow-no-migration` inside the container. `--no-interaction` is required because the deploy webhook has no TTY; without it, data-loss prompts silently abort the migration.

#### WordPress

src/wexample_wex_addon_dev_php/services/wordpress/commands/url/replace.py resolves the current site URL by running `wp option get siteurl` inside the `wordpress_cli` sidecar, then calls `wp search-replace <old> <new> --skip-columns=guid`. When `--new-url` is omitted, the target URL is guessed from the first domain in the runtime config's `app.domains` list.

### Call path

A typical Python service command goes through these steps:

1. The wex CLI resolves the command name (e.g. `symfony/service/install_local`) through the registry built by `PhpAddonManager.get_package_module`, which points at the `wexample_wex_addon_dev_php` package for auto-discovery.
2. The framework constructs an `ExecutionContext` carrying the kernel, IO handle, and the resolved `AppService` for the target service.
3. The command function receives `context` and `service`; it calls `service.addon_manager.docker_exec(service.name, [...])` to run a shell fragment inside the named Docker container (e.g. `{app_project_name}_symfony`).
4. For commands that delegate to the generic Composer routines (Laravel `install_local` and `refresh_lock`, Symfony `install_local`), the inner function is called directly in the same Python frame — `composer__service__install_local.function(context=context, service=service)` — reusing the same `service` so execution happens inside the calling service's container, not the composer container.
5. YAML commands (`maintenance/enable.yml`, `db/migrate.yml`) follow the same resolution path but their shell scripts are executed by the wex YAML runner, which maps `runner: docker` + `service: laravel` to the equivalent `docker exec` call.

## Integration in the Suite

This package is part of the Wexample Suite — a collection of high-quality, modular tools designed to work seamlessly together across multiple languages and environments.

### Related Packages

The suite includes packages for configuration management, file handling, prompts, and more. Each package can be used independently or as part of the integrated suite.

Visit the [Wexample Suite documentation](https://docs.wexample.com) for the complete package ecosystem.

## Dependencies

- attrs: >=23.1.0
- cattrs: >=23.1.0
- wexample-filestate-php: >=6.4.0
- wexample-wex-addon-ai: >=13.0.0
- wexample-wex-addon-app: >=30.0.0
- wexample-wex-addon-dev-javascript: >=8.1.0

## Versioning & Compatibility Policy

Wexample packages follow **Semantic Versioning** (SemVer):

- **MAJOR**: Breaking changes
- **MINOR**: New features, backward compatible
- **PATCH**: Bug fixes, backward compatible

We maintain backward compatibility within major versions and provide clear migration guides for breaking changes.

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

Free to use in both personal and commercial projects.

## About us

[Wexample](https://wexample.com) stands as a cornerstone of the digital ecosystem — a collective of seasoned engineers, researchers, and creators driven by a relentless pursuit of technological excellence. More than a media platform, it has grown into a vibrant community where innovation meets craftsmanship, and where every line of code reflects a commitment to clarity, durability, and shared intelligence.

This packages suite embodies this spirit. Trusted by professionals and enthusiasts alike, it delivers a consistent, high-quality foundation for modern development — open, elegant, and battle-tested. Its reputation is built on years of collaboration, refinement, and rigorous attention to detail, making it a natural choice for those who demand both robustness and beauty in their tools.

Wexample cultivates a culture of mastery. Each package, each contribution carries the mark of a community that values precision, ethics, and innovation — a community proud to shape the future of digital craftsmanship.

## Known Limitations & Roadmap

Current limitations and planned features are tracked in the GitHub issues.

See the [project roadmap](https://github.com/wexample/python-wex_addon_dev_php/issues) for upcoming features and improvements.

## Status & Compatibility

**Maturity**: Production-ready

**Python Support**: >=3.10

**OS Support**: Linux, macOS, Windows

**Status**: Actively maintained

## Useful Links

- **Homepage**: https://github.com/wexample/python-wex-addon-dev-php
- **Documentation**: [docs.wexample.com](https://docs.wexample.com)
- **Issue Tracker**: https://github.com/wexample/python-wex-addon-dev-php/issues
- **Discussions**: https://github.com/wexample/python-wex-addon-dev-php/discussions
- **PyPI**: [pypi.org/project/wexample-wex-addon-dev-php](https://pypi.org/project/wexample-wex-addon-dev-php/)

## Migration Notes

When upgrading between major versions, refer to the migration guides in the documentation.

Breaking changes are clearly documented with upgrade paths and examples.
