Pohoda::__call()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 17
ccs 8
cts 8
cp 1
rs 10
c 0
b 0
f 0
cc 4
nc 4
nop 2
crap 4
1
<?php
2
3
/**
4
 * This file is part of riesenia/pohoda package.
5
 *
6
 * Licensed under the MIT License
7
 * (c) RIESENIA.com
8
 */
9
10
declare(strict_types=1);
11
12
namespace Riesenia;
13
14
use Psr\Container\ContainerInterface;
15
use Riesenia\Pohoda\AbstractAgenda;
16
use Riesenia\Pohoda\Common\OneDirectionalVariablesTrait;
17
18
/**
19
 * Factory for Pohoda objects.
20
 *
21
 * @method Pohoda\AddressBook   createAddressBook(array $data = [])
22
 * @method Pohoda\Bank          createBank(array $data = [])
23
 * @method Pohoda\CashSlip      createCashSlip(array $data = [])
24
 * @method Pohoda\Category      createCategory(array $data = [])
25
 * @method Pohoda\Contract      createContract(array $data = [])
26
 * @method Pohoda\IntDoc        createIntDoc(array $data = [])
27
 * @method Pohoda\IntParam      createIntParam(array $data = [])
28
 * @method Pohoda\Invoice       createInvoice(array $data = [])
29
 * @method Pohoda\IssueSlip     createIssueSlip(array $data = [])
30
 * @method Pohoda\ListRequest   createListRequest(array $data = [])
31
 * @method Pohoda\ListResponse  createListResponse(array $data = [])
32
 * @method Pohoda\Offer         createOffer(array $data = [])
33
 * @method Pohoda\Order         createOrder(array $data = [])
34
 * @method Pohoda\PrintRequest  createPrintRequest(array $data = [])
35
 * @method Pohoda\Receipt       createReceipt(array $data = [])
36
 * @method Pohoda\Stock         createStock(array $data = [])
37
 * @method Pohoda\StockTransfer createStockTransfer(array $data = [])
38
 * @method Pohoda\Storage       createStorage(array $data = [])
39
 * @method Pohoda\Supplier      createSupplier(array $data = [])
40
 * @method Pohoda\UserList      createUserList(array $data = [])
41
 * @method Pohoda\Voucher       createVoucher(array $data = [])
42
 * @method \bool loadAddressBook(string $filename)
43
 * @method \bool loadBank(string $filename)
44
 * @method \bool loadCashSlip(string $filename)
45
 * @method \bool loadCategory(string $filename)
46
 * @method \bool loadContract(string $filename)
47
 * @method \bool loadIntDoc(string $filename)
48
 * @method \bool loadIntParam(string $filename)
49
 * @method \bool loadInvoice(string $filename)
50
 * @method \bool loadIssueSlip(string $filename)
51
 * @method \bool loadListRequest(string $filename)
52
 * @method \bool loadListResponse(string $filename)
53
 * @method \bool loadOffer(string $filename)
54
 * @method \bool loadOrder(string $filename)
55
 * @method \bool loadPrintRequest(string $filename)
56
 * @method \bool loadReceipt(string $filename)
57
 * @method \bool loadStock(string $filename)
58
 * @method \bool loadStockTransfer(string $filename)
59
 * @method \bool loadStorage(string $filename)
60
 * @method \bool loadSupplier(string $filename)
61
 * @method \bool loadUserList(string $filename)
62
 * @method \bool loadVoucher(string $filename)
63
 *
64
 * @link https://www.stormware.cz/pohoda/xml/seznamschemat/   schemas
65
 */
66
class Pohoda
67
{
68
    use OneDirectionalVariablesTrait;
69
70
    protected string $application = 'Pohoda connector';
71
72
    protected bool $isInMemory;
73
74
    protected \XMLWriter $xmlWriter;
75
76
    protected \XMLReader $xmlReader;
77
78
    protected Pohoda\DI\DependenciesFactory $dependenciesFactory;
79
80
    protected string $elementName;
81
82
    protected bool $importRecursive = false;
83
84
    protected readonly Pohoda\Common\CompanyRegistrationNumberInterface $companyRegistrationNumber;
85
86 18
    public function __construct(
87
        string|Pohoda\Common\CompanyRegistrationNumberInterface $companyRegistrationNumber,
88
        ?Pohoda\DI\DependenciesFactory $dependenciesFactory = null,
89
        ?Pohoda\ValueTransformer\SanitizeEncoding $sanitizeEncoding = new Pohoda\ValueTransformer\SanitizeEncoding(new Pohoda\ValueTransformer\Listing()),
90
        ?Pohoda\Common\NamespacesPaths $namespacesPaths = new Pohoda\Common\NamespacesPaths(),
91
        ?Pohoda\Common\OptionsResolver\Normalizers\NormalizerFactory $normalizerFactory = new Pohoda\Common\OptionsResolver\Normalizers\NormalizerFactory(),
92
        ?ContainerInterface $container = null,
93
    ) {
94 18
        $this->companyRegistrationNumber = is_object($companyRegistrationNumber)
0 ignored issues
show
Bug introduced by
The property companyRegistrationNumber is declared read-only in Riesenia\Pohoda.
Loading history...
95
            ? $companyRegistrationNumber
96 18
            : Pohoda\Common\CompanyRegistrationNumber::init($companyRegistrationNumber);
97 18
        $this->dependenciesFactory = is_object($dependenciesFactory)
98 18
            ? $dependenciesFactory
99
            : new Pohoda\DI\DependenciesFactory($namespacesPaths, $sanitizeEncoding, $normalizerFactory, $container);
100
    }
101
102
    /**
103
     * Set the name of the application.
104
     *
105
     * @param string $name
106
     *
107
     * @return void
108
     */
109 1
    public function setApplicationName(string $name): void
110
    {
111 1
        $this->application = $name;
112
    }
113
114
    /**
115
     * Get class listing transformers for content serialization
116
     *
117
     * @return Pohoda\ValueTransformer\Listing
118
     */
119 2
    public function getTransformerListing(): Pohoda\ValueTransformer\Listing
120
    {
121 2
        return $this->dependenciesFactory->getSanitizeEncoding()->getListing();
122
    }
123
124
    /**
125
     * Create and return instance of requested agenda.
126
     *
127
     * @param string              $name
128
     * @param array<string,mixed> $data
129
     *
130
     * @return AbstractAgenda
131
     */
132 3
    public function create(string $name, array $data = []): AbstractAgenda
133
    {
134 3
        return $this->dependenciesFactory
135 3
            ->getAgendaFactory()
136 3
            ->getAgenda($name)
137 3
            ->setDirectionalVariable($this->useOneDirectionalVariables)
138 3
            ->setResolveOptions(true)
139 3
            ->setData($data);
140
    }
141
142
    /**
143
     * Open new XML file for writing.
144
     *
145
     * @param string|null $filename path to output file or null for memory
146
     * @param string      $id
147
     * @param string      $note
148
     *
149
     * @return bool
150
     */
151 4
    public function open(?string $filename, string $id, string $note = ''): bool
152
    {
153 4
        $this->xmlWriter = new \XMLWriter();
154
155 4
        if (is_null($filename)) {
156 3
            $this->isInMemory = true;
157 3
            $this->xmlWriter->openMemory();
158
        } else {
159 1
            $this->isInMemory = false;
160
161 1
            if (!$this->xmlWriter->openUri($filename)) {
162
                // @codeCoverageIgnoreStart
163
                // I cannot test this, because it needs source file somewhere online
164
                return false;
165
            }
166
            // @codeCoverageIgnoreEnd
167
        }
168
169 4
        $this->xmlWriter->startDocument('1.0', $this->dependenciesFactory->getSanitizeEncoding()->getEncoding());
170 4
        $this->xmlWriter->startElementNs('dat', 'dataPack', null);
171
172 4
        $this->xmlWriter->writeAttribute('id', $id);
173 4
        $this->xmlWriter->writeAttribute('ico', $this->companyRegistrationNumber->getCompanyNumber());
174 4
        $this->xmlWriter->writeAttribute('application', $this->application);
175 4
        $this->xmlWriter->writeAttribute('version', '2.0');
176 4
        $this->xmlWriter->writeAttribute('note', $note);
177
178 4
        foreach ($this->dependenciesFactory->getNamespacePaths()->allNamespaces() as $k => $v) {
179
            // put all known namespaces into base element attributes
180 4
            $this->xmlWriter->writeAttributeNs('xmlns', $k, null, $v);
181
        }
182
183 4
        return true;
184
    }
185
186
    /**
187
     * Add item.
188
     *
189
     * @param string $id
190
     * @param AbstractAgenda $agenda
191
     *
192
     * @return void
193
     */
194 4
    public function addItem(string $id, AbstractAgenda $agenda): void
195
    {
196 4
        $this->xmlWriter->startElementNs('dat', 'dataPackItem', null);
197
198 4
        $this->xmlWriter->writeAttribute('id', $id);
199 4
        $this->xmlWriter->writeAttribute('version', '2.0');
200 4
        $this->xmlWriter->writeRaw((string) $agenda->getXML()->asXML());
201 4
        $this->xmlWriter->endElement();
202
203 4
        if (!$this->isInMemory) {
204 1
            $this->xmlWriter->flush();
205
        }
206
    }
207
208
    /**
209
     * End and close XML file.
210
     *
211
     * @return int|string written bytes for file or XML string for memory
212
     */
213 8
    public function close(): int|string
214
    {
215 8
        $this->xmlWriter->endElement();
216
217 8
        return $this->xmlWriter->flush();
218
    }
219
220
    /**
221
     * Load XML file.
222
     *
223
     * @param string $name
224
     * @param string $filename filename or current xml content
225
     *
226
     * @return bool
227
     */
228 4
    public function load(string $name, string $filename): bool
229
    {
230 4
        $this->xmlReader = new \XMLReader();
231
232 4
        if ($this->detectXmlFileHeader($filename)) {
233 1
            if (!@$this->xmlReader->XML($filename)) {
234
                // @codeCoverageIgnoreStart
235
                // cannot create string which will be parsed as XML and crash itself afterward
236
                return false;
237
            }
238
            // @codeCoverageIgnoreEnd
239
        } else {
240 3
            if (!@$this->xmlReader->open($filename)) {
241 1
                return false;
242
            }
243
        }
244
245 3
        $class = $this->dependenciesFactory->getAgendaFactory()->getAgenda($name);
246 3
        $class->setResolveOptions(false);
247 3
        $this->elementName = $class->getImportRoot() ?? throw new \DomainException('Not allowed entity: ' . $name);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $class->getImportRoot() targeting Riesenia\Pohoda\AbstractAgenda::getImportRoot() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
248 3
        $this->importRecursive = $class->canImportRecursive();
249
250 3
        return true;
251
    }
252
253
    /**
254
     * Detect if passed file is in fact XML and not just path
255
     * A bit simple, but it is enough for now
256
     *
257
     * @param string $string
258
     *
259
     * @return bool
260
     */
261 4
    protected function detectXmlFileHeader(string $string): bool
262
    {
263 4
        return str_contains(substr($string, 0, 64), '<?xml');
264
    }
265
266
    /**
267
     * Get next item in loaded file.
268
     *
269
     * @return \SimpleXMLElement|null
270
     */
271 3
    public function next(): ?\SimpleXMLElement
272
    {
273 3
        while (\XMLReader::ELEMENT != $this->xmlReader->nodeType || $this->xmlReader->name !== $this->elementName) {
274 3
            if (!$this->xmlReader->read()) {
275 3
                return null;
276
            }
277
        }
278
279 3
        $xml = new \SimpleXMLElement($this->xmlReader->readOuterXml());
280 3
        $this->importRecursive ? $this->xmlReader->next() : $this->xmlReader->read();
281
282 3
        return $xml;
283
    }
284
285
    /**
286
     * Handle dynamic method calls.
287
     *
288
     * @param string  $method
289
     * @param mixed[] $arguments
290
     *
291
     * @return mixed
292
     */
293 6
    public function __call(string $method, array $arguments)
294
    {
295
        // create<Agenda> method
296 6
        if (\preg_match('/create([A-Z][a-zA-Z0-9]*)/', $method, $matches)) {
297 1
            return \call_user_func([$this, 'create'], $matches[1], $arguments[0] ?? []);
298
        }
299
300
        // load<Agenda> method
301 5
        if (\preg_match('/load([A-Z][a-zA-Z0-9]*)/', $method, $matches)) {
302 4
            if (!isset($arguments[0])) {
303 1
                throw new \DomainException('Filename not set.');
304
            }
305
306 3
            return \call_user_func([$this, 'load'], $matches[1], $arguments[0]);
307
        }
308
309 1
        throw new \BadMethodCallException('Unknown method: ' . $method);
310
    }
311
}
312