Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
72 / 72
100.00% covered (success)
100.00%
41 / 41
CRAP
100.00% covered (success)
100.00%
1 / 1
AbstractAdapter
100.00% covered (success)
100.00%
72 / 72
100.00% covered (success)
100.00%
41 / 41
51
100.00% covered (success)
100.00%
1 / 1
 createAdapter
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
2
 setAuthClient
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setShipType
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setRatesApiUrl
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setTrackingApiUrl
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setUserAgent
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getAuthClient
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getShipType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRatesApiUrl
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTrackingApiUrl
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getUserAgent
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasAuthClient
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasShipType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasRatesApiUrl
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasTrackingApiUrl
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addPackages
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 addPackage
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getPackages
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPackage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasPackages
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasPackage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addTrackingNumbers
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 addTrackingNumber
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 getTrackingNumbers
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasTrackingNumbers
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasTrackingNumber
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setShipTo
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 getShipTo
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasShipTo
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setShipFrom
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 getShipFrom
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasShipFrom
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getResponse
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasResponse
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isSuccess
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 getErrorCode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasErrorCode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getErrorMessage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasErrorMessage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRates
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasRates
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 fetchRates
n/a
0 / 0
n/a
0 / 0
0
 parseRatesResponse
n/a
0 / 0
n/a
0 / 0
0
 getTracking
n/a
0 / 0
n/a
0 / 0
0
 parseTrackingResponse
n/a
0 / 0
n/a
0 / 0
0
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <dev@noladev.com>
8 * @copyright  Copyright (c) 2009-2027 NOLA Interactive, LLC.
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Shipping\Adapter;
16
17use Pop\Http;
18use Pop\Shipping\Address;
19use Pop\Shipping\Auth\AbstractAuthClient;
20use Pop\Shipping\Client\AbstractShippingClient;
21use Pop\Shipping\Package;
22
23/**
24 * Pop shipping adapter abstract class
25 *
26 * @category   Pop
27 * @package    Pop\AbstractAdapter
28 * @author     Nick Sagona, III <dev@noladev.com>
29 * @copyright  Copyright (c) 2009-2027 NOLA Interactive, LLC.
30 * @license    https://www.popphp.org/license     New BSD License
31 * @version    4.0.0
32 */
33abstract class AbstractAdapter extends AbstractShippingClient implements AdapterInterface
34{
35
36    /**
37     * Auth clientL
38     * @var ?AbstractAuthClient
39     */
40    protected ?AbstractAuthClient $authClient = null;
41
42    /**
43     * Rates API URL
44     * @var ?string
45     */
46    protected ?string $ratesApiUrl = null;
47
48    /**
49     * Tracking API URL
50     * @var ?string
51     */
52    protected ?string $trackingApiUrl = null;
53
54    /**
55     * User-Agent
56     * @var string
57     */
58    protected string $userAgent = 'popphp/pop-shipping 4.0.0';
59
60    /**
61     * Ship type (Drop-off, Pick-up, etc.)
62     * @var ?string
63     */
64    protected ?string $shipType = null;
65
66    /**
67     * AbstractAdapter packages
68     * @var Package[]
69     */
70    protected array $packages = [];
71
72    /**
73     * AbstractAdapter tracking numbers
74     * @var array
75     */
76    protected array $trackingNumbers = [];
77
78    /**
79     * Ship to fields
80     *
81     * @var ?Address
82     */
83    protected ?Address $shipTo = null;
84
85    /**
86     * Ship from fields
87     *
88     * @var ?Address
89     */
90    protected ?Address $shipFrom = null;
91
92    /**
93     * API response
94     * @var mixed
95     */
96    protected mixed $response = null;
97
98    /**
99     * Rates
100     * @var array
101     */
102    protected array $rates = [];
103
104    /**
105     * API error code
106     * @var mixed
107     */
108    protected mixed $errorCode = null;
109
110    /**
111     * API error message
112     * @var mixed
113     */
114    protected mixed $errorMessage = null;
115
116    /**
117     * Create shipping adapter
118     *
119     * @param  AbstractAuthClient $authClient
120     * @return static
121     */
122    public static function createAdapter(AbstractAuthClient $authClient): static
123    {
124        $adapter = new static();
125        $adapter->setAuthClient($authClient)
126                ->setProduction($authClient->isProduction());
127
128        if ($authClient->hasAuthToken()) {
129            $adapter->setClient(new Http\Client(
130                Http\Auth::createBearer($authClient->getAuthToken()),
131                [
132                    'base_uri' => $adapter->getApiUrl(),
133                    'type'     => 'application/json'
134                ]
135            ));
136        }
137
138        return $adapter;
139    }
140
141    /**
142     * Set auth client
143     *
144     * @param  AbstractAuthClient $authClient
145     * @return AbstractAdapter
146     */
147    public function setAuthClient(AbstractAuthClient $authClient): AbstractAdapter
148    {
149        $this->authClient = $authClient;
150        return $this;
151    }
152
153    /**
154     * Set ship type
155     *
156     * @param  string $shipType
157     * @return AbstractAdapter
158     */
159    public function setShipType(string $shipType): AbstractAdapter
160    {
161        $this->shipType = $shipType;
162        return $this;
163    }
164
165    /**
166     * Set rates API URL
167     *
168     * @param  string $ratesApiUrl
169     * @return AbstractAdapter
170     */
171    public function setRatesApiUrl(string $ratesApiUrl): AbstractAdapter
172    {
173        $this->ratesApiUrl = $ratesApiUrl;
174        return $this;
175    }
176
177    /**
178     * Set tracking API URL
179     *
180     * @param  string $trackingApiUrl
181     * @return AbstractAdapter
182     */
183    public function setTrackingApiUrl(string $trackingApiUrl): AbstractAdapter
184    {
185        $this->trackingApiUrl = $trackingApiUrl;
186        return $this;
187    }
188
189    /**
190     * Set user-agent
191     *
192     * @param  string $userAgent
193     * @return AbstractAdapter
194     */
195    public function setUserAgent(string $userAgent): AbstractAdapter
196    {
197        $this->userAgent = $userAgent;
198        return $this;
199    }
200
201    /**
202     * Get auth client
203     *
204     * @return ?AbstractAuthClient
205     */
206    public function getAuthClient(): ?AbstractAuthClient
207    {
208        return $this->authClient;
209    }
210
211    /**
212     * Get ship type
213     *
214     * @return ?string
215     */
216    public function getShipType(): ?string
217    {
218        return $this->shipType;
219    }
220
221    /**
222     * Get rates API URL
223     *
224     * @return ?string
225     */
226    public function getRatesApiUrl(): ?string
227    {
228        return $this->ratesApiUrl;
229    }
230
231    /**
232     * Get tracking API URL
233     *
234     * @return ?string
235     */
236    public function getTrackingApiUrl(): ?string
237    {
238        return $this->trackingApiUrl;
239    }
240
241    /**
242     * Get user-agent
243     *
244     * @return string
245     */
246    public function getUserAgent(): string
247    {
248        return $this->userAgent;
249    }
250
251    /**
252     * Has auth client
253     *
254     * @return bool
255     */
256    public function hasAuthClient(): bool
257    {
258        return !empty($this->authClient);
259    }
260
261    /**
262     * Has ship type
263     *
264     * @return bool
265     */
266    public function hasShipType(): bool
267    {
268        return !empty($this->shipType);
269    }
270
271    /**
272     * Has rates API URL
273     *
274     * @return bool
275     */
276    public function hasRatesApiUrl(): bool
277    {
278        return !empty($this->ratesApiUrl);
279    }
280
281    /**
282     * Has tracking API URL
283     *
284     * @return bool
285     */
286    public function hasTrackingApiUrl(): bool
287    {
288        return !empty($this->trackingApiUrl);
289    }
290
291    /**
292     * Add shipping packages
293     *
294     * @param  array $packages
295     * @return AbstractAdapter
296     */
297    public function addPackages(array $packages): AbstractAdapter
298    {
299        foreach ($packages as $package) {
300            $this->addPackage($package);
301        }
302        return $this;
303    }
304
305    /**
306     * Add shipping package
307     *
308     * @param  Package $package
309     * @return AbstractAdapter
310     */
311    public function addPackage(Package $package): AbstractAdapter
312    {
313        if (!$package->hasId()) {
314            $package->setId(uniqid());
315        }
316        $this->packages[$package->getId()] = $package;
317        return $this;
318    }
319
320    /**
321     * Get shipping packages
322     *
323     * @return array
324     */
325    public function getPackages(): array
326    {
327        return $this->packages;
328    }
329
330    /**
331     * Get shipping package
332     *
333     * @param  mixed $id
334     * @return ?Package
335     */
336    public function getPackage(mixed $id): ?Package
337    {
338        return $this->packages[$id] ?? null;
339    }
340
341    /**
342     * Has shipping packages
343     *
344     * @return bool
345     */
346    public function hasPackages(): bool
347    {
348        return (!empty($this->packages));
349    }
350
351    /**
352     * Has shipping package
353     *
354     * @param  mixed $id
355     * @return bool
356     */
357    public function hasPackage(mixed $id): bool
358    {
359        return (isset($this->packages[$id]));
360    }
361
362    /**
363     * Add shipping tracking numbers
364     *
365     * @param  array $trackingNumbers
366     * @return AbstractAdapter
367     */
368    public function addTrackingNumbers(array $trackingNumbers): AbstractAdapter
369    {
370        foreach ($trackingNumbers as $trackingNumber) {
371            $this->addTrackingNumber($trackingNumber);
372        }
373        return $this;
374    }
375
376    /**
377     * Add shipping tracking number
378     *
379     * @param  string $trackingNumber
380     * @return AbstractAdapter
381     */
382    public function addTrackingNumber(string $trackingNumber): AbstractAdapter
383    {
384        if (str_contains($trackingNumber, ',')) {
385            $trackingNumbers = array_filter(array_map('trim', explode(',', $trackingNumber)));
386        } else {
387            $trackingNumbers[] = $trackingNumber;
388        }
389
390        foreach ($trackingNumbers as $trackingNumber) {
391            if (!in_array($trackingNumber, $this->trackingNumbers)) {
392                $this->trackingNumbers[] = $trackingNumber;
393            }
394        }
395
396        return $this;
397    }
398
399    /**
400     * Get shipping packages
401     *
402     * @return array
403     */
404    public function getTrackingNumbers(): array
405    {
406        return $this->trackingNumbers;
407    }
408
409    /**
410     * Has shipping tracking numbers
411     *
412     * @return bool
413     */
414    public function hasTrackingNumbers(): bool
415    {
416        return (!empty($this->trackingNumbers));
417    }
418
419    /**
420     * Has shipping tracking number
421     *
422     * @param  string $trackingNumber
423     * @return bool
424     */
425    public function hasTrackingNumber(string $trackingNumber): bool
426    {
427        return (in_array($trackingNumber, $this->trackingNumbers));
428    }
429
430    /**
431     * Set ship to
432     *
433     * @param  array|Address $shipTo
434     * @return AbstractAdapter
435     */
436    public function setShipTo(array|Address $shipTo): AbstractAdapter
437    {
438        $this->shipTo = !($shipTo instanceof Address) ? new Address($shipTo) : $shipTo;
439        return $this;
440    }
441
442    /**
443     * Get ship to
444     *
445     * @return Address
446     */
447    public function getShipTo(): Address
448    {
449        return $this->shipTo;
450    }
451
452    /**
453     * Has ship to
454     *
455     * @return bool
456     */
457    public function hasShipTo(): bool
458    {
459        return ($this->shipTo !== null);
460    }
461
462    /**
463     * Set ship from
464     *
465     * @param  array|Address $shipFrom
466     * @return AbstractAdapter
467     */
468    public function setShipFrom(array|Address $shipFrom): AbstractAdapter
469    {
470        $this->shipFrom = !($shipFrom instanceof Address) ? new Address($shipFrom) : $shipFrom;
471        return $this;
472    }
473
474    /**
475     * Get ship from
476     *
477     * @return Address
478     */
479    public function getShipFrom(): Address
480    {
481        return $this->shipFrom;
482    }
483
484    /**
485     * Has ship from
486     *
487     * @return bool
488     */
489    public function hasShipFrom(): bool
490    {
491        return ($this->shipFrom !== null);
492    }
493
494    /**
495     * Get response
496     *
497     * @return mixed
498     */
499    public function getResponse(): mixed
500    {
501        return $this->response;
502    }
503
504    /**
505     * Has response
506     *
507     * @return bool
508     */
509    public function hasResponse(): bool
510    {
511        return !empty($this->response);
512    }
513
514    /**
515     * Is success
516     *
517     * @return bool
518     */
519    public function isSuccess(): bool
520    {
521        return (!empty($this->rates) && empty($this->errorCode));
522    }
523
524    /**
525     * Get error code
526     *
527     * @return mixed
528     */
529    public function getErrorCode(): mixed
530    {
531        return $this->errorCode;
532    }
533
534    /**
535     * Has error code
536     *
537     * @return bool
538     */
539    public function hasErrorCode(): bool
540    {
541        return (!empty($this->errorCode));
542    }
543
544    /**
545     * Get error message
546     *
547     * @return mixed
548     */
549    public function getErrorMessage(): mixed
550    {
551        return $this->errorMessage;
552    }
553
554    /**
555     * Has error message
556     *
557     * @return bool
558     */
559    public function hasErrorMessage(): bool
560    {
561        return (!empty($this->errorMessage));
562    }
563
564    /**
565     * Get rates
566     *
567     * @return array
568     */
569    public function getRates(): array
570    {
571        return $this->rates;
572    }
573
574    /**
575     * Has rates
576     *
577     * @return bool
578     */
579    public function hasRates(): bool
580    {
581        return (!empty($this->rates));
582    }
583
584    /**
585     * Fetch rates from the API
586     *
587     * @return array
588     */
589    abstract public function fetchRates(): array;
590
591    /**
592     * Parse rates response
593     *
594     * @return array
595     */
596    abstract public function parseRatesResponse(): array;
597
598    /**
599     * Get tracking
600     *
601     * @param  string|array|null $trackingNumbers
602     * @return array
603     */
604    abstract public function getTracking(string|array|null $trackingNumbers = null): array;
605
606    /**
607     * Parse tracking response
608     *
609     * @return array
610     */
611    abstract public function parseTrackingResponse(): array;
612
613}