Completed
Push — master ( f4d17a...5f656e )
by Chauncey
09:46
created

AppConfig::setApis()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
1
<?php
2
3
namespace Charcoal\App;
4
5
use Exception;
6
use InvalidArgumentException;
7
8
// From PSR-7
9
use Psr\Http\Message\UriInterface;
10
11
// From Slim
12
use Slim\Http\Uri;
13
14
// From 'charcoal-config'
15
use Charcoal\Config\AbstractConfig;
16
17
// From 'charcoal-view'
18
use Charcoal\View\ViewConfig;
19
20
// From 'charcoal-app'
21
use Charcoal\App\Config\CacheConfig;
22
use Charcoal\App\Config\FilesystemConfig;
23
use Charcoal\App\Config\LoggerConfig;
24
25
/**
26
 * Charcoal App configuration
27
 */
28
class AppConfig extends AbstractConfig
29
{
30
    /**
31
     * The application's timezone.
32
     *
33
     * @var string|null
34
     */
35
    private $timezone;
36
37
    /**
38
     * The application's name.
39
     *
40
     * For internal usage.
41
     *
42
     * @var string|null
43
     */
44
    private $projectName;
45
46
    /**
47
     * The base path for the Charcoal installation.
48
     *
49
     * @var string|null
50
     */
51
    private $basePath;
52
53
    /**
54
     * The base URL (public) for the Charcoal installation.
55
     *
56
     * @var UriInterface|null
57
     */
58
    private $baseUrl;
59
60
    /**
61
     * The path to the public / web directory.
62
     *
63
     * @var string|null
64
     */
65
    private $publicPath;
66
67
    /**
68
     * The path to the storage directory.
69
     *
70
     * @var string|null
71
     */
72
    private $storagePath;
73
74
    /**
75
     * Whether the debug mode is enabled (TRUE) or not (FALSE).
76
     *
77
     * @var boolean
78
     */
79
    private $devMode = false;
80
81
    /**
82
     * The application's routes.
83
     *
84
     * @var array
85
     */
86
    private $routes = [];
87
88
    /**
89
     * The application's dynamic routes.
90
     *
91
     * @var array
92
     */
93
    private $routables = [];
94
95
    /**
96
     * The application's handlers.
97
     *
98
     * @var array
99
     */
100
    private $handlers = [];
101
102
    /**
103
     * The application's modules.
104
     *
105
     * @var array
106
     */
107
    private $modules = [];
108
109
    /**
110
     * The application's API credentials and service configsets.
111
     *
112
     * @var array
113
     */
114
    private $apis = [];
115
116
    /**
117
     * The application's caching configset.
118
     *
119
     * @var CacheConfig
120
     */
121
    private $cache;
122
123
    /**
124
     * The application's logging configset.
125
     *
126
     * @var LoggerConfig
127
     */
128
    private $logger;
129
130
    /**
131
     * The application's view/rendering configset.
132
     *
133
     * @var ViewConfig
134
     */
135
    protected $view;
136
137
    /**
138
     * The application's database configsets.
139
     *
140
     * @var array
141
     */
142
    private $databases = [];
143
144
    /**
145
     * The application's default database configset.
146
     *
147
     * @var string
148
     */
149
    private $defaultDatabase;
150
151
    /**
152
     * The application's filesystem configset.
153
     *
154
     * @var FilesystemConfig
155
     */
156
    private $filesystem;
157
158
    /**
159
     * Default app-config values.
160
     *
161
     * @return array
162
     */
163
    public function defaults()
164
    {
165
        /** @var string $baseDir Presume that Charcoal App _is_ the application */
166
        $baseDir = rtrim(realpath(__DIR__.'/../../../'), '/').'/';
167
168
        return [
169
            'project_name'     => '',
170
            'base_path'        => $baseDir,
171
            'public_path'      => null,
172
            'storage_path'     => null,
173
            'timezone'         => 'UTC',
174
            'routes'           => [],
175
            'routables'        => [],
176
            'handlers'         => [],
177
            'modules'          => [],
178
            'cache'            => [],
179
            'logger'           => [],
180
            'view'             => [],
181
            'databases'        => [],
182
            'default_database' => 'default',
183
            'dev_mode'         => false
184
        ];
185
    }
186
187
    /**
188
     * Set the application's absolute root path.
189
     *
190
     * Resolves symlinks with realpath() and ensure trailing slash.
191
     *
192
     * @param  string $path The absolute path to the application's root directory.
193
     * @throws InvalidArgumentException If the argument is not a string.
194
     * @return AppConfig Chainable
195
     */
196
    public function setBasePath($path)
197
    {
198
        if ($path === null) {
199
            throw new InvalidArgumentException(
200
                'The base path is required.'
201
            );
202
        }
203
204
        if (!is_string($path)) {
205
            throw new InvalidArgumentException(
206
                'The base path must be a string'
207
            );
208
        }
209
210
        $this->basePath = rtrim(realpath($path), '\\/').DIRECTORY_SEPARATOR;
211
212
        return $this;
213
    }
214
215
    /**
216
     * Retrieve the application's absolute root path.
217
     *
218
     * @return string The absolute path to the application's root directory.
219
     */
220
    public function basePath()
221
    {
222
        return $this->basePath;
223
    }
224
225
    /**
226
     * Set the application's absolute path to the public web directory.
227
     *
228
     * @param  string $path The path to the application's public directory.
229
     * @throws InvalidArgumentException If the argument is not a string.
230
     * @return AppConfig Chainable
231
     */
232 View Code Duplication
    public function setPublicPath($path)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
233
    {
234
        if ($path === null) {
235
            $this->publicPath = null;
236
237
            return $this;
238
        }
239
240
        if (!is_string($path)) {
241
            throw new InvalidArgumentException(
242
                'The public path must be a string'
243
            );
244
        }
245
246
        $this->publicPath = rtrim(realpath($path), '\\/').DIRECTORY_SEPARATOR;
247
248
        return $this;
249
    }
250
251
    /**
252
     * Retrieve the application's absolute path to the public web directory.
253
     *
254
     * @return string The absolute path to the application's public directory.
255
     */
256
    public function publicPath()
257
    {
258
        if (!isset($this->publicPath)) {
259
            return $this->basePath().'www'.DIRECTORY_SEPARATOR;
260
        }
261
262
        return $this->publicPath;
263
    }
264
265
    /**
266
     * Set the application's absolute path to the storage directory.
267
     *
268
     * @param  string|null $path The path to the application's storage directory.
269
     * @throws InvalidArgumentException If the argument is not a string.
270
     * @return $this
271
     */
272 View Code Duplication
    public function setStoragePath($path)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
273
    {
274
        if ($path === null) {
275
            $this->storagePath = null;
276
277
            return $this;
278
        }
279
280
        if (!is_string($path)) {
281
            throw new InvalidArgumentException(
282
                'The storage path must be a string'
283
            );
284
        }
285
286
        $this->storagePath = rtrim(realpath($path), '\\/').DIRECTORY_SEPARATOR;
287
288
        return $this;
289
    }
290
291
    /**
292
     * Get the path to the storage directory.
293
     *
294
     * Note that the storage space is outside of the public access.
295
     *
296
     * @return string
297
     */
298
    public function storagePath()
299
    {
300
        if (!isset($this->storagePath)) {
301
            return $this->basePath().'uploads'.DIRECTORY_SEPARATOR;
302
        }
303
304
        return $this->storagePath;
305
    }
306
307
    /**
308
     * Set the application's fully qualified base URL to the public web directory.
309
     *
310
     * @param  UriInterface|string $uri The base URI to the application's web directory.
311
     * @return AppConfig Chainable
312
     */
313
    public function setBaseUrl($uri)
314
    {
315
        $this->baseUrl = Uri::createFromString($uri);
0 ignored issues
show
Bug introduced by
It seems like $uri defined by parameter $uri on line 313 can also be of type object<Psr\Http\Message\UriInterface>; however, Slim\Http\Uri::createFromString() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
316
317
        return $this;
318
    }
319
320
    /**
321
     * Retrieve the application's fully qualified base URL to the public web directory.
322
     *
323
     * @return UriInterface|null The base URI to the application's web directory.
324
     */
325
    public function baseUrl()
326
    {
327
        return $this->baseUrl;
328
    }
329
330
    /**
331
     * Set the application's default timezone.
332
     *
333
     * @param  string $timezone The timezone string.
334
     * @throws InvalidArgumentException If the argument is not a string.
335
     * @return AppConfig Chainable
336
     */
337
    public function setTimezone($timezone)
338
    {
339
        if (!is_string($timezone)) {
340
            throw new InvalidArgumentException(
341
                'Timezone must be a string.'
342
            );
343
        }
344
        $this->timezone = $timezone;
345
        return $this;
346
    }
347
348
    /**
349
     * Retrieve the application's default timezone.
350
     *
351
     * Will be used by the PHP date and date-time functions.
352
     *
353
     * @return string
354
     */
355
    public function timezone()
356
    {
357
        if (isset($this->timezone)) {
358
            return $this->timezone;
359
        } else {
360
            return 'UTC';
361
        }
362
    }
363
364
    /**
365
     * Sets the project name.
366
     *
367
     * @param string|null $projectName The project name.
368
     * @throws InvalidArgumentException If the project argument is not a string (or null).
369
     * @return AppConfig Chainable
370
     */
371
    public function setProjectName($projectName)
372
    {
373
        if ($projectName === null) {
374
            $this->projectName = null;
375
            return $this;
376
        }
377
        if (!is_string($projectName)) {
378
            throw new InvalidArgumentException(
379
                'Project name must be a string'
380
            );
381
        }
382
        $this->projectName = $projectName;
383
        return $this;
384
    }
385
386
    /**
387
     * @return string|null
388
     */
389
    public function projectName()
390
    {
391
        if ($this->projectName === null) {
392
            $baseUrl = $this->baseUrl();
393
            if ($baseUrl) {
394
                return $baseUrl->getHost();
395
            }
396
        }
397
        return $this->projectName;
398
    }
399
400
    /**
401
     * @param boolean $devMode The "dev mode" flag.
402
     * @return AppConfig Chainable
403
     */
404
    public function setDevMode($devMode)
405
    {
406
        $this->devMode = !!$devMode;
407
        return $this;
408
    }
409
410
    /**
411
     * @return boolean
412
     */
413
    public function devMode()
414
    {
415
        return !!$this->devMode;
416
    }
417
418
    /**
419
     * @param array $view The view configuration structure to set.
420
     * @return AppConfig Chainable
421
     */
422
    public function setView(array $view)
423
    {
424
        if (!isset($this->view)) {
425
            $this->view = [];
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type object<Charcoal\View\ViewConfig> of property $view.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
426
        }
427
428
        $this->view = array_merge($this->view, $view);
0 ignored issues
show
Documentation Bug introduced by
It seems like array_merge($this->view, $view) of type array is incompatible with the declared type object<Charcoal\View\ViewConfig> of property $view.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
429
430
        return $this;
431
    }
432
433
    /**
434
     * Parse the application's API configuration.
435
     *
436
     * @param  array $apis The API configuration structure to set.
437
     * @return AppConfig Chainable
438
     */
439
    public function setApis(array $apis)
440
    {
441
        if (!isset($this->apis)) {
442
            $this->apis = [];
443
        }
444
445
        $this->apis = array_replace_recursive($this->apis, $apis);
446
447
        return $this;
448
    }
449
450
    /**
451
     * @return array
452
     */
453
    public function apis()
454
    {
455
        return $this->apis;
456
    }
457
458
    /**
459
     * Parse the application's route configuration.
460
     *
461
     * @see    \Charcoal\Admin\Config::setRoutes() For a similar implementation.
462
     * @param  array $routes The route configuration structure to set.
463
     * @return AppConfig Chainable
464
     */
465
    public function setRoutes(array $routes)
466
    {
467
        if (!isset($this->routes)) {
468
            $this->routes = [];
469
        }
470
471
        $toIterate = [ 'templates', 'actions', 'scripts' ];
472
        foreach ($routes as $key => $val) {
473
            if (in_array($key, $toIterate) && isset($this->routes[$key])) {
474
                $this->routes[$key] = array_merge($this->routes[$key], $val);
475
            } else {
476
                $this->routes[$key] = $val;
477
            }
478
        }
479
480
        return $this;
481
    }
482
483
    /**
484
     * @return array
485
     */
486
    public function routes()
487
    {
488
        return $this->routes;
489
    }
490
491
    /**
492
     * @param array $routables The routable configuration structure to set.
493
     * @return AppConfig Chainable
494
     */
495
    public function setRoutables(array $routables)
496
    {
497
        $this->routables = $routables;
498
        return $this;
499
    }
500
501
    /**
502
     * @return array
503
     */
504
    public function routables()
505
    {
506
        return $this->routables;
507
    }
508
509
    /**
510
     * Define custom response and error handlers.
511
     *
512
     * Slim provides five standard handlers:
513
     * - "foundHandler"
514
     * - "notFoundHandler"
515
     * - "notAllowedHandler"
516
     * - "errorHandler"
517
     * - "phpErrorHandler"
518
     *
519
     * @param array $handlers The handlers configuration structure to set.
520
     * @return AppConfig Chainable
521
     */
522
    public function setHandlers(array $handlers)
523
    {
524
        $this->handlers = $handlers;
525
        return $this;
526
    }
527
528
    /**
529
     * @return array
530
     */
531
    public function handlers()
532
    {
533
        return $this->handlers;
534
    }
535
536
    /**
537
     * Set the configuration modules.
538
     *
539
     * The modules are defined in a `key`=>`\Charcoal\App\Module\ModuleConfig` structure.
540
     *
541
     * @param array $modules The module configuration structure to set.
542
     * @return AppConfig Chainable
543
     */
544
    public function setModules(array $modules)
545
    {
546
        $this->modules = $modules;
547
        return $this;
548
    }
549
550
    /**
551
     * @return array
552
     */
553
    public function modules()
554
    {
555
        return $this->modules;
556
    }
557
558
    /**
559
     * @param array|CacheConfig $cache The application global cache config.
560
     * @throws InvalidArgumentException If the argument is not an array or a config.
561
     * @return AppConfig Chainable
562
     */
563 View Code Duplication
    public function setCache($cache)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
564
    {
565
        if ($cache instanceof CacheConfig) {
566
            $this->cache = $cache;
567
            $this->cache->addDelegate($this);
568
        } elseif (is_array($cache)) {
569
            $this->cache = new CacheConfig($cache, [$this]);
570
        } else {
571
            throw new InvalidArgumentException(
572
                'Cache must be an array of config options or a CacheConfig object.'
573
            );
574
        }
575
        return $this;
576
    }
577
578
    /**
579
     * Get the application's global `CacheConfig`.
580
     *
581
     * @return CacheConfig
582
     */
583
    public function cache()
584
    {
585
        return $this->cache;
586
    }
587
588
    /**
589
     * @param array|LoggerConfig $logger The global logger config.
590
     * @throws InvalidArgumentException If the argument is not an array or a config.
591
     * @return AppConfig Chainable
592
     */
593 View Code Duplication
    public function setLogger($logger)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
594
    {
595
        if ($logger instanceof LoggerConfig) {
596
            $this->logger = $logger;
597
            $this->logger->addDelegate($this);
598
        } elseif (is_array($logger)) {
599
            $this->logger = new LoggerConfig($logger, [$this]);
600
        } else {
601
            throw new InvalidArgumentException(
602
                'Logger must be an array of config options or a LoggerConfig object.'
603
            );
604
        }
605
        return $this;
606
    }
607
608
    /**
609
     * Get the application's global `LoggerConfig`
610
     *
611
     * @return LoggerConfig
612
     */
613
    public function logger()
614
    {
615
        return $this->logger;
616
    }
617
618
    /**
619
     * @param array $databases The avaiable databases config.
620
     * @return Config Chainable
621
     */
622
    public function setDatabases(array $databases)
623
    {
624
        $this->databases = $databases;
625
        return $this;
626
    }
627
628
    /**
629
     * @throws Exception If trying to access this method and no databases were set.
630
     * @return array
631
     */
632
    public function databases()
633
    {
634
        if ($this->databases === null) {
635
            throw new Exception(
636
                'Invalid app config: Databases are not set.'
637
            );
638
        }
639
        return $this->databases;
640
    }
641
642
    /**
643
     * @param string $ident The ident of the database to return the configuration of.
644
     * @throws InvalidArgumentException If the ident argument is not a string.
645
     * @throws Exception If trying to access an invalid database.
646
     * @return array
647
     */
648
    public function databaseConfig($ident)
649
    {
650
        if (!is_string($ident)) {
651
            throw new InvalidArgumentException(
652
                'Invalid app config: default database must be a string.'
653
            );
654
        }
655
        $databases = $this->databases();
656
        if (!isset($databases[$ident])) {
657
            throw new Exception(
658
                sprintf('Invalid app config: no database configuration matches "%s".', $ident)
659
            );
660
        }
661
        return $databases[$ident];
662
    }
663
664
    /**
665
     * @param string $defaultDatabase The default database ident.
666
     * @throws InvalidArgumentException If the argument is not a string.
667
     * @return AppConfig Chainable
668
     */
669
    public function setDefaultDatabase($defaultDatabase)
670
    {
671
        if (!is_string($defaultDatabase)) {
672
            throw new InvalidArgumentException(
673
                'Invalid app config: Default database must be a string.'
674
            );
675
        }
676
        $this->defaultDatabase = $defaultDatabase;
677
        return $this;
678
    }
679
680
    /**
681
     * @param string $ident  The database ident.
682
     * @param array  $config The database options.
683
     * @throws InvalidArgumentException If the arguments are invalid.
684
     * @return AppConfig Chainable
685
     */
686
    public function addDatabase($ident, array $config)
687
    {
688
        if (!is_string($ident)) {
689
            throw new InvalidArgumentException(
690
                'Invalid app config: database ident must be a string.'
691
            );
692
        }
693
694
        if ($this->databases === null) {
695
            $this->databases = [];
696
        }
697
        $this->databases[$ident] = $config;
698
        return $this;
699
    }
700
701
    /**
702
     * @throws Exception If trying to access this method before a setter.
703
     * @return mixed
704
     */
705
    public function defaultDatabase()
706
    {
707
        if ($this->defaultDatabase === null) {
708
            throw new Exception(
709
                'Invalid app config: default database is not set.'
710
            );
711
        }
712
        return $this->defaultDatabase;
713
    }
714
715
    /**
716
     * @param array|FilesystemConfig $filesystem The application global cache config.
717
     * @throws InvalidArgumentException If the argument is not an array or a config.
718
     * @return AppConfig Chainable
719
     */
720 View Code Duplication
    public function setFilesystem($filesystem)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
721
    {
722
        if ($filesystem instanceof FilesystemConfig) {
723
            $this->filesystem = $filesystem;
724
            $this->filesystem->addDelegate($this);
725
        } elseif (is_array($filesystem)) {
726
            $this->filesystem = new FileSystemConfig($filesystem, [$this]);
727
        } else {
728
            throw new InvalidArgumentException(
729
                'Filesystem must be an array of config options or a FilesystemConfig object.'
730
            );
731
        }
732
        return $this;
733
    }
734
735
    /**
736
     * Get the application's global `FilesystemConfig`
737
     *
738
     * @return FilesystemConfig
739
     */
740
    public function filesystem()
741
    {
742
        return $this->filesystem;
743
    }
744
}
745