Lines 67.30% 35 / 52
Methods 85.00% 17 / 20
Classes 0.00% 0 / 1
Covered by tests of size
Name Lines Methods CRAP
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 addSql 100.00% 2 / 2 100.00% 1 / 1 1
 addSqlForDbType 0.00% 0 / 4 0.00% 0 / 1 6
 addConfig 100.00% 2 / 2 100.00% 1 / 1 1
 deleteConfig 100.00% 2 / 2 100.00% 1 / 1 1
 renameConfig 100.00% 2 / 2 100.00% 1 / 1 1
 updateConfig 100.00% 2 / 2 100.00% 1 / 1 1
 copyFile 100.00% 3 / 3 100.00% 1 / 1 1
 copyDirectory 100.00% 3 / 3 100.00% 1 / 1 1
 grantPermission 100.00% 2 / 2 100.00% 1 / 1 1
 createUser 0.00% 0 / 11 0.00% 0 / 1 2
 insertFormInput 0.00% 0 / 2 0.00% 0 / 1 2
 addOperation 100.00% 2 / 2 100.00% 1 / 1 1
 getOperations 100.00% 1 / 1 100.00% 1 / 1 1
 getOperationsByType 100.00% 1 / 1 100.00% 1 / 1 1
 getSqlQueries 100.00% 3 / 3 100.00% 1 / 1 2
 getOperationCounts 100.00% 5 / 5 100.00% 1 / 1 2
 toArray 100.00% 1 / 1 100.00% 1 / 1 1
 clear 100.00% 2 / 2 100.00% 1 / 1 1
 count 100.00% 1 / 1 100.00% 1 / 1 1
26class OperationRecorder
27{
28    /** @var OperationInterface[] */
29    private array $operations = [];
30
31    public function __construct(
32        private readonly Configuration $configuration,
33        private readonly ?Filesystem $filesystem = null,
34    ) {
35    }
36
37    /**
38     * Records a SQL query operation.
39     */
40    public function addSql(string $query, string $description = ''): self
41    {
42        $this->operations[] = new SqlOperation($this->configuration, $query, $description);
43        return $this;
44    }
45
46    /**
47     * Records a SQL query operation with database type condition.
48     *
49     * @param string|string[] $dbTypes One or more database types (mysqli, pgsql, sqlite3, sqlsrv)
50     */
51    public function addSqlForDbType(string $query, string|array $dbTypes, string $description = ''): self
52    {
53        $dbTypes = (array) $dbTypes;
54        if (in_array(Database::getType(), $dbTypes, strict: true)) {
55            $this->operations[] = new SqlOperation($this->configuration, $query, $description);
56        }
57        return $this;
58    }
59
60    /**
61     * Records a configuration add operation.
62     */
63    public function addConfig(string $key, mixed $value): self
64    {
65        $this->operations[] = new ConfigAddOperation($this->configuration, $key, $value);
66        return $this;
67    }
68
69    /**
70     * Records a configuration delete operation.
71     */
72    public function deleteConfig(string $key): self
73    {
74        $this->operations[] = new ConfigDeleteOperation($this->configuration, $key);
75        return $this;
76    }
77
78    /**
79     * Records a configuration rename operation.
80     */
81    public function renameConfig(string $oldKey, string $newKey): self
82    {
83        $this->operations[] = new ConfigRenameOperation($this->configuration, $oldKey, $newKey);
84        return $this;
85    }
86
87    /**
88     * Records a configuration update operation.
89     */
90    public function updateConfig(string $key, mixed $value): self
91    {
92        $this->operations[] = new ConfigUpdateOperation($this->configuration, $key, $value);
93        return $this;
94    }
95
96    /**
97     * Records a file copy operation.
98     */
99    public function copyFile(string $source, string $destination, bool $onlyIfExists = true): self
100    {
101        $filesystem = $this->filesystem ?? new Filesystem((string) PMF_ROOT_DIR);
102        $this->operations[] = new FileCopyOperation($filesystem, $source, $destination, $onlyIfExists);
103        return $this;
104    }
105
106    /**
107     * Records a directory copy operation.
108     */
109    public function copyDirectory(string $source, string $destination, bool $onlyIfExists = true): self
110    {
111        $filesystem = $this->filesystem ?? new Filesystem((string) PMF_ROOT_DIR);
112        $this->operations[] = new DirectoryCopyOperation($filesystem, $source, $destination, $onlyIfExists);
113        return $this;
114    }
115
116    /**
117     * Records a permission grant operation.
118     */
119    public function grantPermission(string $name, string $description, int $userId = 1): self
120    {
121        $this->operations[] = new PermissionGrantOperation($this->configuration, $name, $description, $userId);
122        return $this;
123    }
124
125    /**
126     * Records a user creation operation.
127     */
128    public function createUser(
129        string $loginName,
130        #[\SensitiveParameter]
131        string $password,
132        string $displayName,
133        string $email,
134        int $userId,
135        bool $isSuperAdmin = false,
136        string $status = 'protected',
137    ): self {
138        $this->operations[] = new UserCreateOperation(
139            $this->configuration,
140            $loginName,
141            $password,
142            $displayName,
143            $email,
144            $userId,
145            $isSuperAdmin,
146            $status,
147        );
148        return $this;
149    }
150
151    /**
152     * Records a form input insertion operation.
153     *
154     * @param array<string, int|string> $formInput
155     */
156    public function insertFormInput(array $formInput): self
157    {
158        $this->operations[] = new FormInputInsertOperation($this->configuration, $formInput);
159        return $this;
160    }
161
162    /**
163     * Records a custom operation.
164     */
165    public function addOperation(OperationInterface $operation): self
166    {
167        $this->operations[] = $operation;
168        return $this;
169    }
170
171    /**
172     * Returns all recorded operations.
173     *
174     * @return OperationInterface[]
175     */
176    public function getOperations(): array
177    {
178        return $this->operations;
179    }
180
181    /**
182     * Returns operations filtered by type.
183     *
184     * @return OperationInterface[]
185     */
186    public function getOperationsByType(string $type): array
187    {
188        return array_filter($this->operations, static fn(OperationInterface $op) => $op->getType() === $type);
189    }
190
191    /**
192     * Returns all SQL queries.
193     *
194     * @return string[]
195     */
196    public function getSqlQueries(): array
197    {
198        return array_map(static fn(OperationInterface $op) => $op instanceof SqlOperation
199            ? $op->getQuery()
200            : '', $this->getOperationsByType('sql'));
201    }
202
203    /**
204     * Returns the count of operations by type.
205     *
206     * @return array<string, int>
207     */
208    public function getOperationCounts(): array
209    {
210        $counts = [];
211        foreach ($this->operations as $operation) {
212            $type = $operation->getType();
213            $counts[$type] = ($counts[$type] ?? 0) + 1;
214        }
215        return $counts;
216    }
217
218    /**
219     * Returns all operations as an array for dry-run output.
220     *
221     * @return array<int, array<string, mixed>>
222     */
223    public function toArray(): array
224    {
225        return array_map(static fn(OperationInterface $op): array => $op->toArray(), array_values($this->operations));
226    }
227
228    /**
229     * Clears all recorded operations.
230     */
231    public function clear(): self
232    {
233        $this->operations = [];
234        return $this;
235    }
236
237    /**
238     * Returns the total count of operations.
239     */
240    public function count(): int
241    {
242        return count($this->operations);
243    }
244}