1: <?php
2: declare(strict_types=1);
3:
4: namespace om\ICal;
5:
6: /**
7: * An immutable calendar component (RFC 5545, section 3.6): a name, properties and child
8: * components. Any component is represented, including unknown and X- components.
9: *
10: * "with" methods return a modified copy.
11: */
12: final readonly class Component {
13: /**
14: * @param list<Property> $properties
15: * @param list<Component> $components
16: */
17: public function __construct(
18: public string $name,
19: public array $properties = [],
20: public array $components = [],
21: ) {
22: }
23:
24: /**
25: * First property with the name, e.g. property('X-APPLE-STRUCTURED-LOCATION').
26: */
27: public function property(string $name): ?Property {
28: $name = strtoupper($name);
29: foreach ($this->properties as $property) {
30: if ($property->name === $name) {
31: return $property;
32: }
33: }
34: return null;
35: }
36:
37: /**
38: * All properties with the name.
39: *
40: * @return list<Property>
41: */
42: public function properties(string $name): array {
43: $name = strtoupper($name);
44: return array_values(array_filter($this->properties, static fn(Property $property): bool => $property->name === $name));
45: }
46:
47: public function has(string $name): bool {
48: return $this->property($name) !== null;
49: }
50:
51: /**
52: * First child component with the name.
53: */
54: public function component(string $name): ?self {
55: $name = strtoupper($name);
56: foreach ($this->components as $component) {
57: if ($component->name === $name) {
58: return $component;
59: }
60: }
61: return null;
62: }
63:
64: /**
65: * Child components with the name.
66: *
67: * @return list<Component>
68: */
69: public function components(string $name): array {
70: $name = strtoupper($name);
71: return array_values(array_filter($this->components, static fn(Component $component): bool => $component->name === $name));
72: }
73:
74: /**
75: * Add a property.
76: *
77: * @param array<string, string|list<string>>|Parameters $parameters
78: */
79: public function withProperty(Property|string $property, string $value = '', array|Parameters $parameters = []): self {
80: $property = $property instanceof Property ? $property : Property::create($property, $value, $parameters);
81: return new self($this->name, [...$this->properties, $property], $this->components);
82: }
83:
84: /**
85: * Replace all properties with the name by the given one.
86: *
87: * @param array<string, string|list<string>>|Parameters $parameters
88: */
89: public function withReplacedProperty(Property|string $property, string $value = '', array|Parameters $parameters = []): self {
90: $property = $property instanceof Property ? $property : Property::create($property, $value, $parameters);
91: return $this->withoutProperties($property->name)->withProperty($property);
92: }
93:
94: public function withoutProperties(string $name): self {
95: $name = strtoupper($name);
96: return new self($this->name, array_values(array_filter($this->properties, static fn(Property $property): bool => $property->name !== $name)), $this->components);
97: }
98:
99: public function withComponent(self $component): self {
100: return new self($this->name, $this->properties, [...$this->components, $component]);
101: }
102:
103: /**
104: * @param list<Component> $components
105: */
106: public function withComponents(array $components): self {
107: return new self($this->name, $this->properties, $components);
108: }
109: }
110: