- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
349 lines
8.3 KiB
PHP
349 lines
8.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\Discovery\ValueObjects;
|
|
|
|
use App\Framework\Core\ValueObjects\Byte;
|
|
use App\Framework\Filesystem\ValueObjects\FilePath;
|
|
use ArrayIterator;
|
|
use Countable;
|
|
use IteratorAggregate;
|
|
|
|
/**
|
|
* Immutable collection of TemplateMapping value objects
|
|
*
|
|
* Provides type-safe operations for template collections with filtering,
|
|
* searching, and memory-efficient operations.
|
|
*/
|
|
final readonly class TemplateCollection implements Countable, IteratorAggregate
|
|
{
|
|
/** @var array<TemplateMapping> */
|
|
private array $templates;
|
|
|
|
public function __construct(TemplateMapping ...$templates)
|
|
{
|
|
$this->templates = array_values($templates);
|
|
}
|
|
|
|
/**
|
|
* Add a template to the collection
|
|
*/
|
|
public function add(TemplateMapping $template): self
|
|
{
|
|
$templates = $this->templates;
|
|
$templates[] = $template;
|
|
|
|
return new self(...$templates);
|
|
}
|
|
|
|
/**
|
|
* Add multiple templates to the collection
|
|
*/
|
|
public function addMany(TemplateMapping ...$templates): self
|
|
{
|
|
return new self(...array_merge($this->templates, $templates));
|
|
}
|
|
|
|
/**
|
|
* Filter templates by name pattern
|
|
*/
|
|
public function filterByName(string $namePattern): self
|
|
{
|
|
$filtered = array_filter(
|
|
$this->templates,
|
|
fn (TemplateMapping $template) => $template->matchesName($namePattern)
|
|
);
|
|
|
|
return new self(...$filtered);
|
|
}
|
|
|
|
/**
|
|
* Filter templates by type
|
|
*/
|
|
public function filterByType(string $type): self
|
|
{
|
|
$filtered = array_filter(
|
|
$this->templates,
|
|
fn (TemplateMapping $template) => $template->isType($type)
|
|
);
|
|
|
|
return new self(...$filtered);
|
|
}
|
|
|
|
/**
|
|
* Filter templates by directory
|
|
*/
|
|
public function filterByDirectory(string $directory): self
|
|
{
|
|
$normalizedDirectory = rtrim($directory, '/');
|
|
|
|
$filtered = array_filter(
|
|
$this->templates,
|
|
fn (TemplateMapping $template) => dirname($template->path->toString()) === $normalizedDirectory
|
|
);
|
|
|
|
return new self(...$filtered);
|
|
}
|
|
|
|
/**
|
|
* Filter templates by extension
|
|
*/
|
|
public function filterByExtension(string $extension): self
|
|
{
|
|
$extension = ltrim($extension, '.');
|
|
|
|
$filtered = array_filter(
|
|
$this->templates,
|
|
fn (TemplateMapping $template) => $template->getExtension() === $extension
|
|
);
|
|
|
|
return new self(...$filtered);
|
|
}
|
|
|
|
/**
|
|
* Find template by exact name and type
|
|
*/
|
|
public function findExact(string $name, string $type = 'view'): ?TemplateMapping
|
|
{
|
|
foreach ($this->templates as $template) {
|
|
if ($template->name === $name && $template->type === $type) {
|
|
return $template;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Find template by name (any type)
|
|
*/
|
|
public function findByName(string $name): ?TemplateMapping
|
|
{
|
|
foreach ($this->templates as $template) {
|
|
if ($template->name === $name) {
|
|
return $template;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Find template by file path
|
|
*/
|
|
public function findByPath(FilePath $path): ?TemplateMapping
|
|
{
|
|
foreach ($this->templates as $template) {
|
|
if ($template->path->equals($path)) {
|
|
return $template;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get templates grouped by type
|
|
* @return array<string, self>
|
|
*/
|
|
public function groupByType(): array
|
|
{
|
|
$grouped = [];
|
|
|
|
foreach ($this->templates as $template) {
|
|
if (! isset($grouped[$template->type])) {
|
|
$grouped[$template->type] = [];
|
|
}
|
|
$grouped[$template->type][] = $template;
|
|
}
|
|
|
|
return array_map(
|
|
fn (array $templates) => new self(...$templates),
|
|
$grouped
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get templates grouped by directory
|
|
* @return array<string, self>
|
|
*/
|
|
public function groupByDirectory(): array
|
|
{
|
|
$grouped = [];
|
|
|
|
foreach ($this->templates as $template) {
|
|
$directory = dirname($template->path->toString());
|
|
if (! isset($grouped[$directory])) {
|
|
$grouped[$directory] = [];
|
|
}
|
|
$grouped[$directory][] = $template;
|
|
}
|
|
|
|
return array_map(
|
|
fn (array $templates) => new self(...$templates),
|
|
$grouped
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get unique template names
|
|
* @return array<int, string>
|
|
*/
|
|
public function getUniqueNames(): array
|
|
{
|
|
$names = array_map(fn (TemplateMapping $template) => $template->name, $this->templates);
|
|
|
|
return array_unique($names);
|
|
}
|
|
|
|
/**
|
|
* Get unique template types
|
|
* @return array<int, string>
|
|
*/
|
|
public function getUniqueTypes(): array
|
|
{
|
|
$types = array_map(fn (TemplateMapping $template) => $template->type, $this->templates);
|
|
|
|
return array_unique($types);
|
|
}
|
|
|
|
/**
|
|
* Get unique directories
|
|
*/
|
|
public function getUniqueDirectories(): array
|
|
{
|
|
$directories = array_map(
|
|
fn (TemplateMapping $template) => dirname($template->path->toString()),
|
|
$this->templates
|
|
);
|
|
|
|
return array_unique($directories);
|
|
}
|
|
|
|
/**
|
|
* Remove duplicate templates based on unique identifier
|
|
*/
|
|
public function deduplicate(): self
|
|
{
|
|
$seen = [];
|
|
$unique = [];
|
|
|
|
foreach ($this->templates as $template) {
|
|
$key = $template->getUniqueId();
|
|
if (! isset($seen[$key])) {
|
|
$seen[$key] = true;
|
|
$unique[] = $template;
|
|
}
|
|
}
|
|
|
|
return new self(...$unique);
|
|
}
|
|
|
|
/**
|
|
* Sort templates by name
|
|
*/
|
|
public function sortedByName(): self
|
|
{
|
|
$sorted = $this->templates;
|
|
usort($sorted, fn (TemplateMapping $a, TemplateMapping $b) => $a->name <=> $b->name);
|
|
|
|
return new self(...$sorted);
|
|
}
|
|
|
|
/**
|
|
* Sort templates by type, then by name
|
|
*/
|
|
public function sortedByTypeAndName(): self
|
|
{
|
|
$sorted = $this->templates;
|
|
usort($sorted, function (TemplateMapping $a, TemplateMapping $b) {
|
|
$typeComparison = $a->type <=> $b->type;
|
|
|
|
return $typeComparison !== 0 ? $typeComparison : $a->name <=> $b->name;
|
|
});
|
|
|
|
return new self(...$sorted);
|
|
}
|
|
|
|
/**
|
|
* Check if collection is empty
|
|
*/
|
|
public function isEmpty(): bool
|
|
{
|
|
return empty($this->templates);
|
|
}
|
|
|
|
/**
|
|
* Get first template or null
|
|
*/
|
|
public function first(): ?TemplateMapping
|
|
{
|
|
return $this->templates[0] ?? null;
|
|
}
|
|
|
|
/**
|
|
* Get last template or null
|
|
*/
|
|
public function last(): ?TemplateMapping
|
|
{
|
|
return end($this->templates) ?: null;
|
|
}
|
|
|
|
/**
|
|
* Convert to array of TemplateMapping objects
|
|
*/
|
|
public function toArray(): array
|
|
{
|
|
return $this->templates;
|
|
}
|
|
|
|
/**
|
|
* Convert to legacy array format for backward compatibility
|
|
*/
|
|
public function toLegacyArray(): array
|
|
{
|
|
$legacy = [];
|
|
|
|
foreach ($this->templates as $template) {
|
|
// Group by template name with variants by type
|
|
if (! isset($legacy[$template->name])) {
|
|
$legacy[$template->name] = [];
|
|
}
|
|
$legacy[$template->name][$template->type] = $template->path->toString();
|
|
}
|
|
|
|
return $legacy;
|
|
}
|
|
|
|
/**
|
|
* Get memory footprint of entire collection
|
|
*/
|
|
public function getMemoryFootprint(): Byte
|
|
{
|
|
$totalBytes = 0;
|
|
foreach ($this->templates as $template) {
|
|
$totalBytes += $template->getMemoryFootprint()->toBytes();
|
|
}
|
|
|
|
// Add overhead for collection structure
|
|
$totalBytes += count($this->templates) * 8; // approximate pointer overhead
|
|
|
|
return Byte::fromBytes($totalBytes);
|
|
}
|
|
|
|
// Countable interface implementation
|
|
|
|
public function count(): int
|
|
{
|
|
return count($this->templates);
|
|
}
|
|
|
|
// IteratorAggregate interface implementation
|
|
|
|
public function getIterator(): ArrayIterator
|
|
{
|
|
return new ArrayIterator($this->templates);
|
|
}
|
|
}
|