feat(Deployment): Integrate Ansible deployment via PHP deployment pipeline

- Create AnsibleDeployStage using framework's Process module for secure command execution
- Integrate AnsibleDeployStage into DeploymentPipelineCommands for production deployments
- Add force_deploy flag support in Ansible playbook to override stale locks
- Use PHP deployment module as orchestrator (php console.php deploy:production)
- Fix ErrorAggregationInitializer to use Environment class instead of $_ENV superglobal

Architecture:
- BuildStage → AnsibleDeployStage → HealthCheckStage for production
- Process module provides timeout, error handling, and output capture
- Ansible playbook supports rollback via rollback-git-based.yml
- Zero-downtime deployments with health checks
This commit is contained in:
2025-10-26 14:08:07 +01:00
parent a90263d3be
commit 3b623e7afb
170 changed files with 19888 additions and 575 deletions

View File

@@ -0,0 +1,117 @@
<?php
declare(strict_types=1);
namespace App\Framework\Core\ValueObjects;
/**
* Phone number value object
*
* Validates and formats phone numbers in E.164 format
* E.164: +[country code][subscriber number]
* Example: +4917612345678
*/
final readonly class PhoneNumber
{
public function __construct(
public string $value
) {
if (!$this->isValid($value)) {
throw new \InvalidArgumentException("Invalid phone number format: {$value}. Must be in E.164 format (+country code + number)");
}
}
public static function fromString(string $value): self
{
return new self($value);
}
/**
* Create from international format (+country code + number)
*/
public static function fromInternational(string $countryCode, string $number): self
{
$cleaned = preg_replace('/[^0-9]/', '', $number);
if (empty($cleaned)) {
throw new \InvalidArgumentException('Phone number cannot be empty');
}
return new self("+{$countryCode}{$cleaned}");
}
public function toString(): string
{
return $this->value;
}
public function __toString(): string
{
return $this->value;
}
/**
* Validate E.164 format
* - Must start with +
* - Followed by 7-15 digits
* - No spaces or special characters
*/
private function isValid(string $value): bool
{
// E.164 format: +[country code][number]
// Max 15 digits total
if (!str_starts_with($value, '+')) {
return false;
}
$numbers = substr($value, 1);
if (!ctype_digit($numbers)) {
return false;
}
$length = strlen($numbers);
return $length >= 7 && $length <= 15;
}
public function equals(self $other): bool
{
return $this->value === $other->value;
}
/**
* Get country code from phone number
* Note: This is a simple extraction, not validation against actual country codes
*/
public function getCountryCode(): string
{
// Extract 1-3 digits after +
preg_match('/^\+(\d{1,3})/', $this->value, $matches);
return $matches[1] ?? '';
}
/**
* Get subscriber number (without country code)
*/
public function getSubscriberNumber(): string
{
$countryCode = $this->getCountryCode();
return substr($this->value, strlen($countryCode) + 1);
}
/**
* Format for display (e.g., +49 176 12345678)
*/
public function toDisplayFormat(): string
{
$countryCode = $this->getCountryCode();
$subscriber = $this->getSubscriberNumber();
// Format subscriber number in groups
$formatted = '+' . $countryCode . ' ';
$formatted .= chunk_split($subscriber, 3, ' ');
return rtrim($formatted);
}
}