├── src ├── Console │ └── Commands │ │ ├── stubs │ │ └── config-validation.stub │ │ ├── ValidationMakeCommand.php │ │ └── ValidateConfigCommand.php ├── Exceptions │ ├── DirectoryNotFoundException.php │ ├── InvalidConfigValueException.php │ ├── NoValidationFilesFoundException.php │ └── ConfigValidatorException.php ├── Facades │ └── ConfigValidator.php ├── Providers │ └── ConfigValidatorProvider.php ├── Services │ ├── Rule.php │ ├── ValidationRepository.php │ └── ConfigValidator.php └── Traits │ └── LoadsConfigValidationFiles.php ├── stubs └── config-validation │ ├── view.php │ ├── hashing.php │ ├── cors.php │ ├── services.php │ ├── app.php │ ├── session.php │ ├── broadcasting.php │ ├── filesystems.php │ ├── auth.php │ ├── mail.php │ ├── cache.php │ ├── queue.php │ ├── logging.php │ └── database.php ├── LICENSE.md ├── resources └── views │ └── validate-config.blade.php ├── composer.json └── README.md /src/Console/Commands/stubs/config-validation.stub: -------------------------------------------------------------------------------- 1 | rules([]), 7 | ]; 8 | -------------------------------------------------------------------------------- /src/Exceptions/DirectoryNotFoundException.php: -------------------------------------------------------------------------------- 1 | rules(['array']), 7 | 8 | Rule::make('compiled')->rules(['string']), 9 | ]; 10 | -------------------------------------------------------------------------------- /stubs/config-validation/hashing.php: -------------------------------------------------------------------------------- 1 | rules(['string', 'in:bcrypt,argon,argon2id']), 7 | 8 | Rule::make('bcrypt')->rules(['array']), 9 | Rule::make('bcrypt.rounds')->rules(['integer']), 10 | 11 | Rule::make('argon')->rules(['array']), 12 | Rule::make('argon.memory')->rules(['integer']), 13 | Rule::make('argon.threads')->rules(['integer']), 14 | Rule::make('argon.time')->rules(['integer']), 15 | ]; 16 | -------------------------------------------------------------------------------- /stubs/config-validation/cors.php: -------------------------------------------------------------------------------- 1 | rules(['array']), 7 | 8 | Rule::make('allowed_methods')->rules(['array']), 9 | 10 | Rule::make('allowed_origins')->rules(['array']), 11 | 12 | Rule::make('allowed_origins_patterns')->rules(['array']), 13 | 14 | Rule::make('allowed_headers')->rules(['array']), 15 | 16 | Rule::make('max_age')->rules(['integer']), 17 | 18 | Rule::make('supports_credentials')->rules(['bool']), 19 | 20 | ]; 21 | -------------------------------------------------------------------------------- /src/Facades/ConfigValidator.php: -------------------------------------------------------------------------------- 1 | errors() 11 | * 12 | * @see \AshAllenDesign\ConfigValidator\Services\ConfigValidator 13 | */ 14 | class ConfigValidator extends Facade 15 | { 16 | /** 17 | * Get the registered name of the component. 18 | * 19 | * @return string 20 | */ 21 | protected static function getFacadeAccessor(): string 22 | { 23 | return 'config-validator'; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /stubs/config-validation/services.php: -------------------------------------------------------------------------------- 1 | rules(['array']), 7 | Rule::make('mailgun.domain')->rules(['string', 'nullable']), 8 | Rule::make('mailgun.secret')->rules(['string', 'nullable']), 9 | Rule::make('mailgun.endpoint')->rules(['string']), 10 | 11 | Rule::make('postmark')->rules(['array']), 12 | Rule::make('postmark.token')->rules(['string', 'nullable']), 13 | 14 | Rule::make('ses')->rules(['array']), 15 | Rule::make('ses.key')->rules(['string', 'nullable']), 16 | Rule::make('ses.secret')->rules(['string', 'nullable']), 17 | Rule::make('ses.region')->rules(['string']), 18 | ]; 19 | -------------------------------------------------------------------------------- /stubs/config-validation/app.php: -------------------------------------------------------------------------------- 1 | rules(['string']), 7 | 8 | Rule::make('env')->rules(['string']), 9 | 10 | Rule::make('debug')->rules(['bool']), 11 | 12 | Rule::make('url')->rules(['url']), 13 | 14 | Rule::make('asset_url')->rules(['url', 'nullable']), 15 | 16 | Rule::make('timezone')->rules(['string']), 17 | 18 | Rule::make('locale')->rules(['string']), 19 | 20 | Rule::make('faker_locale')->rules(['string']), 21 | 22 | Rule::make('key')->rules(['string']), 23 | 24 | Rule::make('cipher')->rules(['string']), 25 | 26 | Rule::make('providers')->rules(['array']), 27 | 28 | Rule::make('aliases')->rules(['array']), 29 | ]; 30 | -------------------------------------------------------------------------------- /stubs/config-validation/session.php: -------------------------------------------------------------------------------- 1 | rules(['string', 'in:file,cookie,database,apc,memcached,redis,dynamodb,array']), 7 | 8 | Rule::make('lifetime')->rules(['integer']), 9 | 10 | Rule::make('expire_on_close')->rules(['bool']), 11 | 12 | Rule::make('encrypt')->rules(['bool']), 13 | 14 | Rule::make('files')->rules(['string']), 15 | 16 | Rule::make('connection')->rules(['string', 'nullable']), 17 | 18 | Rule::make('table')->rules(['string']), 19 | 20 | Rule::make('store')->rules(['string', 'nullable']), 21 | 22 | Rule::make('lottery')->rules(['array']), 23 | 24 | Rule::make('cookie')->rules(['string']), 25 | 26 | Rule::make('path')->rules(['string']), 27 | 28 | Rule::make('domain')->rules(['string', 'nullable']), 29 | 30 | Rule::make('secure')->rules(['bool', 'nullable']), 31 | 32 | Rule::make('http_only')->rules(['bool']), 33 | 34 | Rule::make('same_site')->rules(['string', 'nullable', 'in:lax,strict,none']), 35 | ]; 36 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Ashley Allen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /stubs/config-validation/broadcasting.php: -------------------------------------------------------------------------------- 1 | rules(['string', 'in:pusher,redis,log,null']), 7 | 8 | Rule::make('connections')->rules(['array']), 9 | 10 | Rule::make('connections.pusher')->rules(['array']), 11 | 12 | Rule::make('connections.pusher.driver')->rules(['string']), 13 | 14 | Rule::make('connections.pusher.key')->rules(['string']), 15 | 16 | Rule::make('connections.pusher.secret')->rules(['string']), 17 | 18 | Rule::make('connections.pusher.app_id')->rules(['string']), 19 | 20 | Rule::make('connections.pusher.options')->rules(['array']), 21 | 22 | Rule::make('connections.pusher.options.cluster')->rules(['string']), 23 | 24 | Rule::make('connections.pusher.options.useTLS')->rules(['bool']), 25 | 26 | Rule::make('connections.redis')->rules(['array']), 27 | 28 | Rule::make('connections.redis.driver')->rules(['string']), 29 | 30 | Rule::make('connections.redis.connection')->rules(['string']), 31 | 32 | Rule::make('connections.log')->rules(['array']), 33 | 34 | Rule::make('connections.log.driver')->rules(['string']), 35 | 36 | Rule::make('connections.null')->rules(['array']), 37 | 38 | Rule::make('connections.null.driver')->rules(['string']), 39 | ]; 40 | -------------------------------------------------------------------------------- /stubs/config-validation/filesystems.php: -------------------------------------------------------------------------------- 1 | rules(['string']), 7 | 8 | Rule::make('cloud')->rules(['string']), 9 | 10 | Rule::make('disks')->rules(['array']), 11 | 12 | Rule::make('disks.local')->rules(['array']), 13 | Rule::make('disks.local.driver')->rules(['string']), 14 | Rule::make('disks.local.root')->rules(['string']), 15 | 16 | Rule::make('disks.public')->rules(['array']), 17 | Rule::make('disks.public.driver')->rules(['string']), 18 | Rule::make('disks.public.root')->rules(['string']), 19 | Rule::make('disks.public.url')->rules(['string']), 20 | Rule::make('disks.public.visibility')->rules(['string']), 21 | 22 | Rule::make('disks.s3')->rules(['array']), 23 | Rule::make('disks.s3.driver')->rules(['string']), 24 | Rule::make('disks.s3.key')->rules(['string', 'nullable']), 25 | Rule::make('disks.s3.secret')->rules(['string', 'nullable']), 26 | Rule::make('disks.s3.region')->rules(['string', 'nullable']), 27 | Rule::make('disks.s3.bucket')->rules(['string', 'nullable']), 28 | Rule::make('disks.s3.url')->rules(['string', 'nullable']), 29 | Rule::make('disks.s3.endpoint')->rules(['string', 'nullable']), 30 | 31 | Rule::make('links')->rules(['array']), 32 | ]; 33 | -------------------------------------------------------------------------------- /resources/views/validate-config.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
Config validation failed!
3 | 4 |
{{ count($allErrors) }} errors found in your application:
5 |
6 | @foreach ($allErrors as $configField => $errors) 7 |
8 |
9 | {{ $configField }} 10 | 11 | Value: 12 | @if (config($configField)) 13 | @if(is_array(config($configField))) 14 | {{ json_encode(config($configField, JSON_PRETTY_PRINT)) }} 15 | @else 16 | {{ config($configField) }} 17 | @endif 18 | @else 19 | [empty field] 20 | @endif 21 | 22 |
23 | @foreach ($errors as $error) 24 |
- {{ $error }}
25 | @endforeach 26 |
27 | @endforeach 28 |
29 |
30 | -------------------------------------------------------------------------------- /src/Providers/ConfigValidatorProvider.php: -------------------------------------------------------------------------------- 1 | app->alias(ConfigValidator::class, 'config-validator'); 20 | } 21 | 22 | /** 23 | * Bootstrap any application services. 24 | * 25 | * @return void 26 | */ 27 | public function boot(): void 28 | { 29 | // Publish the default config validation rules. 30 | $this->publishes([ 31 | __DIR__.'/../../stubs/config-validation' => base_path('config-validation'), 32 | ], 'config-validator-defaults'); 33 | 34 | if ($this->app->runningInConsole()) { 35 | $this->loadViewsFrom(__DIR__.'/../../resources/views', 'config-validator'); 36 | 37 | $this->commands([ 38 | ValidateConfigCommand::class, 39 | ValidationMakeCommand::class, 40 | ]); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /stubs/config-validation/auth.php: -------------------------------------------------------------------------------- 1 | rules(['array']), 7 | 8 | Rule::make('defaults.guard')->rules(['string']), 9 | 10 | Rule::make('defaults.passwords')->rules(['string']), 11 | 12 | Rule::make('guards')->rules(['array']), 13 | 14 | Rule::make('guards.web')->rules(['array']), 15 | 16 | Rule::make('guards.web.driver')->rules(['string']), 17 | 18 | Rule::make('guards.web.provider')->rules(['string']), 19 | 20 | Rule::make('guards.api')->rules(['array']), 21 | 22 | Rule::make('guards.api.driver')->rules(['string']), 23 | 24 | Rule::make('guards.api.provider')->rules(['string']), 25 | 26 | Rule::make('guards.api.hash')->rules(['bool']), 27 | 28 | Rule::make('providers')->rules(['array']), 29 | 30 | Rule::make('providers.users')->rules(['array']), 31 | 32 | Rule::make('providers.users.driver')->rules(['string', 'in:eloquent,database']), 33 | 34 | Rule::make('providers.users.model')->rules(['string']), 35 | 36 | Rule::make('passwords')->rules(['array']), 37 | 38 | Rule::make('passwords.users')->rules(['array']), 39 | 40 | Rule::make('passwords.users.provider')->rules(['string']), 41 | 42 | Rule::make('passwords.users.table')->rules(['string']), 43 | 44 | Rule::make('passwords.users.expire')->rules(['integer']), 45 | 46 | Rule::make('passwords.users.throttle')->rules(['integer']), 47 | 48 | Rule::make('password_timeout')->rules(['integer']), 49 | ]; 50 | -------------------------------------------------------------------------------- /src/Console/Commands/ValidationMakeCommand.php: -------------------------------------------------------------------------------- 1 | resolveStubPath('/stubs/config-validation.stub'); 38 | } 39 | 40 | /** 41 | * Resolve the fully-qualified path to the stub. 42 | * 43 | * @param string $stub 44 | * @return string 45 | */ 46 | protected function resolveStubPath(string $stub): string 47 | { 48 | return file_exists($customPath = $this->laravel->basePath(trim($stub, '/'))) 49 | ? $customPath 50 | : __DIR__.$stub; 51 | } 52 | 53 | /** 54 | * Get the destination class path. 55 | * 56 | * @param string $name 57 | * @return string 58 | */ 59 | protected function getPath($name): string 60 | { 61 | return $this->laravel->basePath() 62 | .'/config-validation/' 63 | .str_replace('\\', '/', $this->argument('name')).'.php'; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ashallendesign/laravel-config-validator", 3 | "description": "A package for validating your Laravel app's config.", 4 | "type": "library", 5 | "homepage": "https://github.com/ash-jc-allen/laravel-config-validator", 6 | "keywords": [ 7 | "ashallendesign", 8 | "config", 9 | "validation", 10 | "laravel" 11 | ], 12 | "license": "MIT", 13 | "authors": [ 14 | { 15 | "name": "Ash Allen", 16 | "email": "mail@ashallendesign.co.uk" 17 | } 18 | ], 19 | "require": { 20 | "php": "^8.2", 21 | "illuminate/console": "^10.0|^11.0|^12.0", 22 | "illuminate/container": "^10.0|^11.0|^12.0", 23 | "illuminate/validation": "^10.0|^11.0|^12.0", 24 | "illuminate/view": "^10.0|^11.0|^12.0", 25 | "nunomaduro/termwind": "^1.6|^2.0", 26 | "ext-json": "*" 27 | }, 28 | "require-dev": { 29 | "mockery/mockery": "^1.0", 30 | "larastan/larastan": "^1.0|^2.0|^3.0", 31 | "orchestra/testbench": "^8.0|^9.0|^10.0", 32 | "phpunit/phpunit": "^9.0|^10.5|^11.5.3" 33 | }, 34 | "autoload": { 35 | "psr-4": { 36 | "AshAllenDesign\\ConfigValidator\\": "src/" 37 | } 38 | }, 39 | "autoload-dev": { 40 | "psr-4": { 41 | "AshAllenDesign\\ConfigValidator\\Tests\\": "tests/" 42 | } 43 | }, 44 | "extra": { 45 | "laravel": { 46 | "providers": [ 47 | "AshAllenDesign\\ConfigValidator\\Providers\\ConfigValidatorProvider" 48 | ], 49 | "aliases": { 50 | "ConfigValidator": "AshAllenDesign\\ConfigValidator\\Facades\\ConfigValidator" 51 | } 52 | } 53 | }, 54 | "scripts": { 55 | "test": "vendor/bin/phpunit", 56 | "larastan": "vendor/bin/phpstan analyse" 57 | }, 58 | "minimum-stability": "dev", 59 | "prefer-stable": true 60 | } 61 | -------------------------------------------------------------------------------- /stubs/config-validation/mail.php: -------------------------------------------------------------------------------- 1 | rules(['string', 'in:smtp,sendmail,mailgun,ses,postmark,log,array']), 7 | 8 | Rule::make('mailers')->rules(['array']), 9 | 10 | Rule::make('mailers.smtp')->rules(['array']), 11 | Rule::make('mailers.smtp.transport')->rules(['string']), 12 | Rule::make('mailers.smtp.host')->rules(['string']), 13 | Rule::make('mailers.smtp.port')->rules(['integer']), 14 | Rule::make('mailers.smtp.encryption')->rules(['string', 'nullable']), 15 | Rule::make('mailers.smtp.username')->rules(['string', 'nullable']), 16 | Rule::make('mailers.smtp.password')->rules(['string', 'nullable']), 17 | Rule::make('mailers.smtp.timeout')->rules(['integer', 'nullable']), 18 | Rule::make('mailers.smtp.auth_mode')->rules(['string', 'nullable']), 19 | 20 | Rule::make('mailers.ses')->rules(['array']), 21 | Rule::make('mailers.ses.transport')->rules(['string']), 22 | 23 | Rule::make('mailers.mailgun')->rules(['array']), 24 | Rule::make('mailers.mailgun.transport')->rules(['string']), 25 | 26 | Rule::make('mailers.postmark')->rules(['array']), 27 | Rule::make('mailers.postmark.transport')->rules(['string']), 28 | 29 | Rule::make('mailers.sendmail')->rules(['array']), 30 | Rule::make('mailers.sendmail.transport')->rules(['string']), 31 | Rule::make('mailers.sendmail.path')->rules(['string']), 32 | 33 | Rule::make('mailers.log')->rules(['array']), 34 | Rule::make('mailers.log.transport')->rules(['string']), 35 | Rule::make('mailers.log.channel')->rules(['string', 'nullable']), 36 | 37 | Rule::make('mailers.array')->rules(['array']), 38 | Rule::make('mailers.array.transport')->rules(['string']), 39 | 40 | Rule::make('from')->rules(['array']), 41 | Rule::make('from.address')->rules(['email']), 42 | Rule::make('from.name')->rules(['string']), 43 | 44 | Rule::make('markdown')->rules(['array']), 45 | Rule::make('markdown.theme')->rules(['string']), 46 | Rule::make('markdown.paths')->rules(['array']), 47 | ]; 48 | -------------------------------------------------------------------------------- /stubs/config-validation/cache.php: -------------------------------------------------------------------------------- 1 | rules(['string', 'in:apc,array,database,file,memcached,redis,dynamodb']), 7 | 8 | Rule::make('stores')->rules(['array']), 9 | 10 | Rule::make('stores.apc')->rules(['array']), 11 | Rule::make('stores.apc.driver')->rules(['string']), 12 | 13 | Rule::make('stores.array')->rules(['array']), 14 | Rule::make('stores.array.driver')->rules(['string']), 15 | Rule::make('stores.array.serialize')->rules(['bool']), 16 | 17 | Rule::make('stores.database')->rules(['array']), 18 | Rule::make('stores.database.driver')->rules(['string']), 19 | Rule::make('stores.database.table')->rules(['string']), 20 | Rule::make('stores.database.connection')->rules(['string', 'nullable']), 21 | 22 | Rule::make('stores.file')->rules(['array']), 23 | Rule::make('stores.file.driver')->rules(['string']), 24 | Rule::make('stores.file.path')->rules(['string']), 25 | 26 | Rule::make('stores.memcached')->rules(['array']), 27 | Rule::make('stores.memcached.driver')->rules(['string']), 28 | Rule::make('stores.memcached.persistent_id')->rules(['string', 'nullable']), 29 | Rule::make('stores.memcached.sasl')->rules(['array']), 30 | Rule::make('stores.memcached.options')->rules(['array']), 31 | Rule::make('stores.memcached.servers')->rules(['array']), 32 | 33 | Rule::make('stores.redis')->rules(['array']), 34 | Rule::make('stores.redis.driver')->rules(['string']), 35 | Rule::make('stores.redis.connection')->rules(['string']), 36 | 37 | Rule::make('stores.dynamodb')->rules(['array']), 38 | Rule::make('stores.dynamodb.driver')->rules(['string']), 39 | Rule::make('stores.dynamodb.key')->rules(['string', 'nullable']), 40 | Rule::make('stores.dynamodb.secret')->rules(['string', 'nullable']), 41 | Rule::make('stores.dynamodb.region')->rules(['string']), 42 | Rule::make('stores.dynamodb.table')->rules(['string']), 43 | Rule::make('stores.dynamodb.endpoint')->rules(['string', 'nullable']), 44 | 45 | Rule::make('prefix')->rules(['string']), 46 | ]; 47 | -------------------------------------------------------------------------------- /stubs/config-validation/queue.php: -------------------------------------------------------------------------------- 1 | rules(['string', 'in:sync,database,beanstalkd,sqs,redis,null']), 7 | 8 | Rule::make('connections')->rules(['array']), 9 | 10 | Rule::make('connections.sync')->rules(['array']), 11 | Rule::make('connections.sync.driver')->rules(['string']), 12 | 13 | Rule::make('connections.database')->rules(['array']), 14 | Rule::make('connections.database.driver')->rules(['string']), 15 | Rule::make('connections.database.table')->rules(['string']), 16 | Rule::make('connections.database.queue')->rules(['string']), 17 | Rule::make('connections.database.retry_after')->rules(['integer']), 18 | 19 | Rule::make('connections.beanstalkd')->rules(['array']), 20 | Rule::make('connections.beanstalkd.driver')->rules(['string']), 21 | Rule::make('connections.beanstalkd.host')->rules(['string']), 22 | Rule::make('connections.beanstalkd.queue')->rules(['string']), 23 | Rule::make('connections.beanstalkd.retry_after')->rules(['integer']), 24 | Rule::make('connections.beanstalkd.block_for')->rules(['integer']), 25 | 26 | Rule::make('connections.sqs')->rules(['array']), 27 | Rule::make('connections.sqs.driver')->rules(['string']), 28 | Rule::make('connections.sqs.key')->rules(['string', 'nullable']), 29 | Rule::make('connections.sqs.secret')->rules(['string', 'nullable']), 30 | Rule::make('connections.sqs.prefix')->rules(['string']), 31 | Rule::make('connections.sqs.queue')->rules(['string']), 32 | Rule::make('connections.sqs.suffix')->rules(['string', 'nullable']), 33 | Rule::make('connections.sqs.region')->rules(['string']), 34 | 35 | Rule::make('connections.redis')->rules(['array']), 36 | Rule::make('connections.redis.driver')->rules(['string']), 37 | Rule::make('connections.redis.connection')->rules(['string']), 38 | Rule::make('connections.redis.queue')->rules(['string']), 39 | Rule::make('connections.redis.retry_after')->rules(['integer', 'nullable']), 40 | Rule::make('connections.redis.block_for')->rules(['integer', 'nullable']), 41 | 42 | Rule::make('failed')->rules(['array']), 43 | Rule::make('failed.driver')->rules(['string']), 44 | Rule::make('failed.database')->rules(['string']), 45 | Rule::make('failed.table')->rules(['string']), 46 | ]; 47 | -------------------------------------------------------------------------------- /stubs/config-validation/logging.php: -------------------------------------------------------------------------------- 1 | rules(['string']), 7 | 8 | Rule::make('channels')->rules(['array']), 9 | 10 | Rule::make('channels.stack')->rules(['array']), 11 | Rule::make('channels.stack.driver')->rules(['string']), 12 | Rule::make('channels.stack.channels')->rules(['array']), 13 | Rule::make('channels.stack.ignore_exceptions')->rules(['bool']), 14 | 15 | Rule::make('channels.single')->rules(['array']), 16 | Rule::make('channels.single.driver')->rules(['string']), 17 | Rule::make('channels.single.path')->rules(['string']), 18 | Rule::make('channels.single.level')->rules(['string']), 19 | 20 | Rule::make('channels.daily')->rules(['array']), 21 | Rule::make('channels.daily.driver')->rules(['string']), 22 | Rule::make('channels.daily.path')->rules(['string']), 23 | Rule::make('channels.daily.level')->rules(['string']), 24 | Rule::make('channels.daily.days')->rules(['integer']), 25 | 26 | Rule::make('channels.slack')->rules(['array']), 27 | Rule::make('channels.slack.driver')->rules(['string']), 28 | Rule::make('channels.slack.url')->rules(['string', 'nullable']), 29 | Rule::make('channels.slack.username')->rules(['string']), 30 | Rule::make('channels.slack.emoji')->rules(['string']), 31 | Rule::make('channels.slack.level')->rules(['string']), 32 | 33 | Rule::make('channels.papertrail')->rules(['array']), 34 | Rule::make('channels.papertrail.driver')->rules(['string']), 35 | Rule::make('channels.papertrail.level')->rules(['string']), 36 | Rule::make('channels.papertrail.handler')->rules(['string']), 37 | Rule::make('channels.papertrail.handler_with')->rules(['array']), 38 | 39 | Rule::make('channels.stderr')->rules(['array']), 40 | Rule::make('channels.stderr.driver')->rules(['string']), 41 | Rule::make('channels.stderr.handler')->rules(['string']), 42 | Rule::make('channels.stderr.formatter')->rules(['string', 'nullable']), 43 | Rule::make('channels.stderr.with')->rules(['array']), 44 | 45 | Rule::make('channels.syslog')->rules(['array']), 46 | Rule::make('channels.syslog.driver')->rules(['string']), 47 | Rule::make('channels.syslog.level')->rules(['string']), 48 | 49 | Rule::make('channels.errorlog')->rules(['array']), 50 | Rule::make('channels.errorlog.driver')->rules(['string']), 51 | Rule::make('channels.errorlog.level')->rules(['string']), 52 | 53 | Rule::make('channels.null')->rules(['array']), 54 | Rule::make('channels.null.driver')->rules(['string']), 55 | Rule::make('channels.null.handler')->rules(['string']), 56 | 57 | Rule::make('channels.emergency')->rules(['array']), 58 | Rule::make('channels.emergency.path')->rules(['string']), 59 | ]; 60 | -------------------------------------------------------------------------------- /src/Services/Rule.php: -------------------------------------------------------------------------------- 1 | 26 | */ 27 | private array $rules = []; 28 | 29 | /** 30 | * The custom messages being used when validating the 31 | * config field. 32 | * 33 | * @var array 34 | */ 35 | private array $messages = []; 36 | 37 | /** 38 | * @var string[] 39 | */ 40 | private array $environments = []; 41 | 42 | /** 43 | * Rule constructor. 44 | * 45 | * @param string $fieldName 46 | */ 47 | public function __construct(string $fieldName) 48 | { 49 | $this->fieldName = $fieldName; 50 | } 51 | 52 | /** 53 | * A helper method used for creating a new rule. 54 | * 55 | * @param string $fieldName 56 | * @return Rule 57 | */ 58 | public static function make(string $fieldName): Rule 59 | { 60 | return new self($fieldName); 61 | } 62 | 63 | /** 64 | * Set the rules used for validating the config. 65 | * 66 | * @param array $rules 67 | * @return $this 68 | */ 69 | public function rules(array $rules): self 70 | { 71 | $this->rules = array_merge($this->rules, $rules); 72 | 73 | return $this; 74 | } 75 | 76 | /** 77 | * Set the custom messages used when validating the 78 | * config. 79 | * 80 | * @param array $messages 81 | * @return $this 82 | */ 83 | public function messages(array $messages): self 84 | { 85 | $this->messages = array_merge($this->messages, $messages); 86 | 87 | return $this; 88 | } 89 | 90 | /** 91 | * @param string[] $environments 92 | */ 93 | public function environments(array $environments): self 94 | { 95 | $this->environments = array_merge($this->environments, $environments); 96 | 97 | return $this; 98 | } 99 | 100 | /** 101 | * Get the config field name that this rule relates to. 102 | * 103 | * @return string 104 | */ 105 | public function getFieldName(): string 106 | { 107 | return $this->fieldName; 108 | } 109 | 110 | /** 111 | * Get the validation rules set within this rule. 112 | * 113 | * @return array 114 | */ 115 | public function getRules(): array 116 | { 117 | return $this->rules; 118 | } 119 | 120 | /** 121 | * Get the validation messages set within this rule. 122 | * 123 | * @return array 124 | */ 125 | public function getMessages(): array 126 | { 127 | return $this->messages; 128 | } 129 | 130 | /** 131 | * Get the app environments set within this rule. 132 | * 133 | * @return string[] 134 | */ 135 | public function getEnvironments(): array 136 | { 137 | return $this->environments; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/Traits/LoadsConfigValidationFiles.php: -------------------------------------------------------------------------------- 1 | 19 | * 20 | * @throws DirectoryNotFoundException 21 | * @throws NoValidationFilesFoundException 22 | */ 23 | private function getValidationFiles(array $configFiles = [], ?string $validationFolderPath = null): array 24 | { 25 | $files = []; 26 | 27 | $folderPath = $this->determineFolderPath($validationFolderPath); 28 | $configFileNames = $this->determineFilesToRead($configFiles); 29 | 30 | $configValidationFiles = Finder::create()->files()->name($configFileNames)->in($folderPath); 31 | 32 | if (! $configValidationFiles->count()) { 33 | throw new NoValidationFilesFoundException('No config validation files were found inside the directory.'); 34 | } 35 | 36 | foreach ($configValidationFiles as $file) { 37 | $directory = $this->getNestedDirectory($file, $folderPath); 38 | 39 | $files[$directory.basename($file->getRealPath(), '.php')] = $file->getRealPath(); 40 | } 41 | 42 | return $files; 43 | } 44 | 45 | /** 46 | * If a custom validation folder path has been set then 47 | * get the full path. Otherwise, return the default 48 | * path in the config/validation folder. If the 49 | * folder does not exist, an exception will 50 | * be thrown. 51 | * 52 | * @param string|null $validationFolderPath 53 | * @return string 54 | * 55 | * @throws DirectoryNotFoundException 56 | */ 57 | protected function determineFolderPath(?string $validationFolderPath = null): string 58 | { 59 | $path = base_path($validationFolderPath ?? 'config-validation'); 60 | 61 | if ($folderPath = realpath($path)) { 62 | return $folderPath; 63 | } 64 | 65 | throw new DirectoryNotFoundException('The directory '.$path.' does not exist.'); 66 | } 67 | 68 | /** 69 | * Get the configuration file nesting path. 70 | * 71 | * @param SplFileInfo $file 72 | * @param string $configPath 73 | * @return string 74 | */ 75 | protected function getNestedDirectory(SplFileInfo $file, string $configPath): string 76 | { 77 | $directory = $file->getPath(); 78 | 79 | if ($nested = trim(str_replace($configPath, '', $directory), DIRECTORY_SEPARATOR)) { 80 | $nested = str_replace(DIRECTORY_SEPARATOR, '.', $nested).'.'; 81 | } 82 | 83 | return $nested; 84 | } 85 | 86 | /** 87 | * If the no specific files were defined, we will read 88 | * all of the files in the directory. Otherwise, we 89 | * only read the files specified. For example, if 90 | * ['cache', 'auth'] were passed in, we would 91 | * return ['cache.php', 'auth.php']. 92 | * 93 | * @param string[] $configFiles 94 | * @return string[] 95 | */ 96 | protected function determineFilesToRead(array $configFiles = []): array 97 | { 98 | if (empty($configFiles)) { 99 | return ['*.php']; 100 | } 101 | 102 | return array_map(static function (string $configValue): string { 103 | return $configValue.'.php'; 104 | }, $configFiles); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Services/ValidationRepository.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | private array $configValues = []; 17 | 18 | /** 19 | * An array of rules that are to be used for validating 20 | * the config values. 21 | * 22 | * @var array> 23 | */ 24 | private array $rules = []; 25 | 26 | /** 27 | * An array of custom messages that are to be used when 28 | * validating the config values. 29 | * 30 | * @var array 31 | */ 32 | private array $messages = []; 33 | 34 | /** 35 | * Add the new Rules' data to the repository so that we 36 | * can read the data later when validating. When doing 37 | * this, we check that the rule is supposed to run in 38 | * this environment. Any rules set to run in other 39 | * environments won't be stored and will just be 40 | * skipped. 41 | * 42 | * @param string $key 43 | * @param Rule[] $rules 44 | */ 45 | public function push(string $key, array $rules): void 46 | { 47 | foreach ($rules as $field => $rule) { 48 | if (! $this->shouldValidateUsingThisRule($rule)) { 49 | continue; 50 | } 51 | 52 | $configKey = $key.'.'.$rule->getFieldName(); 53 | 54 | // Add the rules for the field to the repository. 55 | $this->rules[$configKey] = $rule->getRules(); 56 | 57 | // Add the current config values for the field to the repository. 58 | $this->fetchCurrentConfigValues($key, $rule); 59 | 60 | // Add any custom messages for the field to the repository. 61 | foreach ($rule->getMessages() as $messageField => $message) { 62 | $this->messages[$configKey.'.'.$messageField] = $message; 63 | } 64 | } 65 | } 66 | 67 | /** 68 | * Return the class' rules, config values and messages 69 | * as an array. 70 | * 71 | * @return array 72 | */ 73 | public function asArray(): array 74 | { 75 | return [ 76 | 'rules' => $this->rules, 77 | 'messages' => $this->messages, 78 | 'config_values' => $this->configValues, 79 | ]; 80 | } 81 | 82 | /** 83 | * Determine whether if we should be storing the rule 84 | * to use for validation. We do this by checking if 85 | * an environment has been explicitly defined on 86 | * the rule. If it hasn't, we can add the rule. 87 | * If it has, we can only add the rule if the 88 | * environment matches. 89 | * 90 | * @param Rule $rule 91 | * @return bool 92 | */ 93 | private function shouldValidateUsingThisRule(Rule $rule): bool 94 | { 95 | $environments = $rule->getEnvironments(); 96 | 97 | if (empty($environments)) { 98 | return true; 99 | } 100 | 101 | return in_array(app()->environment(), $environments, true); 102 | } 103 | 104 | /** 105 | * Fetch the current config values that are set in the 106 | * system and then add them to the repository for 107 | * validating. 108 | * 109 | * @param string $key 110 | * @param Rule $rule 111 | */ 112 | private function fetchCurrentConfigValues(string $key, Rule $rule): void 113 | { 114 | Arr::set( 115 | array: $this->configValues[$key], 116 | key: $rule->getFieldName(), 117 | value: config($key.'.'.$rule->getFieldName()), 118 | ); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/Console/Commands/ValidateConfigCommand.php: -------------------------------------------------------------------------------- 1 | configValidator = $configValidator; 49 | } 50 | 51 | /** 52 | * Execute the console command. 53 | * 54 | * @return int 55 | * 56 | * @throws DirectoryNotFoundException 57 | * @throws InvalidConfigValueException 58 | * @throws NoValidationFilesFoundException 59 | */ 60 | public function handle(): int 61 | { 62 | try { 63 | $this->configValidator 64 | ->throwExceptionOnFailure(false) 65 | ->run($this->determineFilesToValidate(), $this->option('path')); 66 | } catch (DirectoryNotFoundException|NoValidationFilesFoundException $exception) { 67 | $this->displayErrorMessage($exception->getMessage()); 68 | 69 | return self::FAILURE; 70 | } 71 | 72 | if (! empty($this->configValidator->errors())) { 73 | render(view('config-validator::validate-config', [ 74 | 'allErrors' => $this->configValidator->errors(), 75 | ])); 76 | 77 | return self::FAILURE; 78 | } 79 | 80 | $this->displaySuccessfulValidationMessage(); 81 | 82 | return self::SUCCESS; 83 | } 84 | 85 | /** 86 | * Determine the config files that should be validated. 87 | * We do this by mapping any comma-separated 'files' 88 | * inputs to an array of strings that can be 89 | * passed to the ConfigValidator object. 90 | * 91 | * @return string[] 92 | */ 93 | private function determineFilesToValidate(): array 94 | { 95 | $filesToValidate = []; 96 | 97 | foreach ($this->option('files') as $fileOption) { 98 | if (Str::contains($fileOption, ',')) { 99 | $exploded = explode(',', $fileOption); 100 | 101 | $filesToValidate = array_merge($filesToValidate, $exploded); 102 | 103 | continue; 104 | } 105 | 106 | $filesToValidate = array_merge($filesToValidate, [$fileOption]); 107 | } 108 | 109 | return $filesToValidate; 110 | } 111 | 112 | private function displaySuccessfulValidationMessage(): void 113 | { 114 | render(<<<'HTML' 115 |
116 | Config validation passed! 117 |
118 | HTML); 119 | } 120 | 121 | private function displayErrorMessage(string $message): void 122 | { 123 | render(<<$message 125 | HTML); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /stubs/config-validation/database.php: -------------------------------------------------------------------------------- 1 | rules(['string']), 7 | 8 | Rule::make('connections')->rules(['array']), 9 | 10 | Rule::make('connections.sqlite')->rules(['array']), 11 | Rule::make('connections.sqlite.driver')->rules(['string']), 12 | Rule::make('connections.sqlite.url')->rules(['string', 'nullable']), 13 | Rule::make('connections.sqlite.database')->rules(['string']), 14 | Rule::make('connections.sqlite.prefix')->rules(['string']), 15 | Rule::make('connections.sqlite.foreign_key_constraints')->rules(['bool']), 16 | 17 | Rule::make('connections.mysql')->rules(['array']), 18 | Rule::make('connections.mysql.driver')->rules(['string']), 19 | Rule::make('connections.mysql.url')->rules(['string', 'nullable']), 20 | Rule::make('connections.mysql.host')->rules(['string']), 21 | Rule::make('connections.mysql.port')->rules(['string']), 22 | Rule::make('connections.mysql.database')->rules(['string']), 23 | Rule::make('connections.mysql.username')->rules(['string']), 24 | Rule::make('connections.mysql.password')->rules(['string']), 25 | Rule::make('connections.mysql.unix_socket')->rules(['string']), 26 | Rule::make('connections.mysql.charset')->rules(['string']), 27 | Rule::make('connections.mysql.collation')->rules(['string']), 28 | Rule::make('connections.mysql.prefix')->rules(['string']), 29 | Rule::make('connections.mysql.prefix_indexes')->rules(['bool']), 30 | Rule::make('connections.mysql.strict')->rules(['bool']), 31 | Rule::make('connections.mysql.engine')->rules(['string', 'nullable']), 32 | Rule::make('connections.mysql.options')->rules(['array']), 33 | 34 | Rule::make('connections.pgsql')->rules(['array']), 35 | Rule::make('connections.pgsql.driver')->rules(['string']), 36 | Rule::make('connections.pgsql.url')->rules(['string', 'nullable']), 37 | Rule::make('connections.pgsql.host')->rules(['string']), 38 | Rule::make('connections.pgsql.port')->rules(['string']), 39 | Rule::make('connections.pgsql.database')->rules(['string']), 40 | Rule::make('connections.pgsql.username')->rules(['string']), 41 | Rule::make('connections.pgsql.password')->rules(['string']), 42 | Rule::make('connections.pgsql.charset')->rules(['string']), 43 | Rule::make('connections.pgsql.prefix')->rules(['string']), 44 | Rule::make('connections.pgsql.prefix_indexes')->rules(['bool']), 45 | Rule::make('connections.pgsql.schema')->rules(['string']), 46 | Rule::make('connections.pgsql.sslmode')->rules(['string']), 47 | 48 | Rule::make('connections.sqlsrv')->rules(['array']), 49 | Rule::make('connections.sqlsrv.driver')->rules(['string']), 50 | Rule::make('connections.sqlsrv.url')->rules(['string', 'nullable']), 51 | Rule::make('connections.sqlsrv.host')->rules(['string']), 52 | Rule::make('connections.sqlsrv.port')->rules(['string']), 53 | Rule::make('connections.sqlsrv.database')->rules(['string']), 54 | Rule::make('connections.sqlsrv.username')->rules(['string']), 55 | Rule::make('connections.sqlsrv.password')->rules(['string']), 56 | Rule::make('connections.sqlsrv.charset')->rules(['string']), 57 | Rule::make('connections.sqlsrv.prefix')->rules(['string']), 58 | Rule::make('connections.sqlsrv.prefix_indexes')->rules(['bool']), 59 | 60 | Rule::make('migrations')->rules(['string']), 61 | 62 | Rule::make('redis')->rules(['array']), 63 | 64 | Rule::make('redis.client')->rules(['string']), 65 | 66 | Rule::make('redis.options')->rules(['array']), 67 | Rule::make('redis.options.cluster')->rules(['string']), 68 | Rule::make('redis.options.prefix')->rules(['string']), 69 | 70 | Rule::make('redis.default')->rules(['array']), 71 | Rule::make('redis.default.url')->rules(['string', 'nullable']), 72 | Rule::make('redis.default.host')->rules(['string']), 73 | Rule::make('redis.default.password')->rules(['string', 'nullable']), 74 | Rule::make('redis.default.port')->rules(['string']), 75 | Rule::make('redis.default.database')->rules(['alpha_dash']), 76 | 77 | Rule::make('redis.cache')->rules(['array']), 78 | Rule::make('redis.cache.url')->rules(['string', 'nullable']), 79 | Rule::make('redis.cache.host')->rules(['string']), 80 | Rule::make('redis.cache.password')->rules(['string', 'nullable']), 81 | Rule::make('redis.cache.port')->rules(['string']), 82 | Rule::make('redis.cache.database')->rules(['alpha_dash']), 83 | ]; 84 | -------------------------------------------------------------------------------- /src/Services/ConfigValidator.php: -------------------------------------------------------------------------------- 1 | 28 | */ 29 | private array $errors = []; 30 | 31 | /** 32 | * Specifies whether if an exception should be thrown 33 | * if the config validation fails. 34 | * 35 | * @var bool 36 | */ 37 | private bool $throwExceptionOnFailure = true; 38 | 39 | /** 40 | * ConfigValidator constructor. 41 | * 42 | * @param ValidationRepository|null $validationRepository 43 | */ 44 | public function __construct(?ValidationRepository $validationRepository = null) 45 | { 46 | $this->validationRepository = $validationRepository ?? new ValidationRepository(); 47 | } 48 | 49 | /** 50 | * Return the validation error messages. 51 | * 52 | * @return array 53 | */ 54 | public function errors(): array 55 | { 56 | return $this->errors; 57 | } 58 | 59 | /** 60 | * Determine whether an exception should be thrown if 61 | * the validation fails. 62 | * 63 | * @param bool $throwException 64 | * @return ConfigValidator 65 | */ 66 | public function throwExceptionOnFailure(bool $throwException = true): self 67 | { 68 | $this->throwExceptionOnFailure = $throwException; 69 | 70 | return $this; 71 | } 72 | 73 | /** 74 | * Handle the loading of the config validation files 75 | * and then validate the config. 76 | * 77 | * @param string[] $configFiles 78 | * @param string|null $validationFolderPath 79 | * @return bool 80 | * 81 | * @throws InvalidConfigValueException 82 | * @throws DirectoryNotFoundException 83 | * @throws NoValidationFilesFoundException 84 | */ 85 | public function run(array $configFiles = [], ?string $validationFolderPath = null): bool 86 | { 87 | $validationFiles = $this->getValidationFiles($configFiles, $validationFolderPath); 88 | 89 | foreach ($validationFiles as $key => $path) { 90 | $ruleSet = require $path; 91 | 92 | $this->validationRepository->push($key, $ruleSet); 93 | } 94 | 95 | return $this->runValidator(); 96 | } 97 | 98 | /** 99 | * Validate config values with rules that are passed in inline rather 100 | * than being read from a file in the filesystem. 101 | * 102 | * @param array> $ruleGroups 103 | * 104 | * @throws InvalidConfigValueException 105 | */ 106 | public function runInline(array $ruleGroups): bool 107 | { 108 | foreach ($ruleGroups as $configKey => $rules) { 109 | $this->validationRepository->push($configKey, $rules); 110 | } 111 | 112 | return $this->runValidator(); 113 | } 114 | 115 | /** 116 | * Validate the config values against the config rules 117 | * that have been set. If throwExceptionOnFailure is 118 | * set to true, the validator's first error message 119 | * will be used as the message in the thrown 120 | * exception. 121 | * 122 | * @return bool 123 | * 124 | * @throws InvalidConfigValueException 125 | */ 126 | private function runValidator(): bool 127 | { 128 | $ruleSet = $this->validationRepository->asArray(); 129 | 130 | // Build an associative array of the keys that we are validating. We 131 | // can pass this to the validator so that the config key names 132 | // are preserved in the error messages and not changed. 133 | $attributes = array_combine( 134 | array_keys($ruleSet['rules']), 135 | array_keys($ruleSet['rules']), 136 | ); 137 | 138 | $validator = Validator::make( 139 | $ruleSet['config_values'], 140 | $ruleSet['rules'], 141 | $ruleSet['messages'], 142 | $attributes, 143 | ); 144 | 145 | if ($validator->fails()) { 146 | $this->errors = $validator->errors()->messages(); 147 | 148 | if ($this->throwExceptionOnFailure) { 149 | throw new InvalidConfigValueException($validator->errors()->first()); 150 | } 151 | 152 | return false; 153 | } 154 | 155 | return true; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |

6 | Latest Version on Packagist 7 | Build Status 8 | Total Downloads 9 | PHP from Packagist 10 | GitHub license 11 |

12 | 13 | ## Table of Contents 14 | 15 | - [Overview](#overview) 16 | - [Installation](#installation) 17 | - [Requirements](#requirements) 18 | - [Install the Package](#install-the-package) 19 | - [Publishing the Default Rulesets](#publishing-the-default-rulesets) 20 | - [Usage](#usage) 21 | - [Creating a Validation Ruleset](#creating-a-validation-ruleset) 22 | - [Using the Generator Command](#using-the-generator-command) 23 | - [Ruleset Location](#ruleset-location) 24 | - [Adding Rules to a RuleSet](#adding-rules-to-a-ruleset) 25 | - [Custom Validation Error Messages](#custom-validation-error-messages) 26 | - [Only Running in Specific App Environments](#only-running-in-specific-app-environments) 27 | - [Running the Validation](#running-the-validation) 28 | - [Running the Validation Manually](#running-the-validation-manually) 29 | - [Only Running on Selected Config Files](#only-running-on-selected-config-files) 30 | - [Custom Folder Path](#custom-folder-path) 31 | - [Using the Command](#using-the-command) 32 | - [Only Running on Selected Config Files (Command)](#only-running-on-selected-config-files-command) 33 | - [Custom Folder Path (Command)](#custom-folder-path-command) 34 | - [Using a Service Provider](#using-a-service-provider) 35 | - [Throwing and Preventing Exceptions](#throwing-and-preventing-exceptions) 36 | - [Facade](#facade) 37 | - [Security](#security) 38 | - [Contribution](#contribution) 39 | - [Changelog](#changelog) 40 | - [Upgrading](#upgrading) 41 | - [License](#license) 42 | 43 | ## Overview 44 | 45 | A Laravel package that allows you to validate your config values and environment. 46 | 47 | ## Installation 48 | 49 | ### Requirements 50 | The package has been developed and tested to work with the following minimum requirements: 51 | 52 | - PHP 8.0 53 | - Laravel 8 54 | 55 | ### Install the Package 56 | You can install the package via Composer: 57 | 58 | ```bash 59 | composer require ashallendesign/laravel-config-validator 60 | ``` 61 | 62 | ### Publishing the Default Rulesets 63 | 64 | To get you started with validating your app's config, Laravel Config Validator comes with some default rulesets. To start 65 | using these rulesets, you can publish them using the following command: 66 | 67 | ```bash 68 | php artisan vendor:publish --tag=config-validator-defaults 69 | ``` 70 | 71 | The above command will copy the validation files and place in a ` config-validation ` folder in your project's root. These rules 72 | are just to get you started, so there are likely going to be rule in the files that don't apply to your app. So, once you've 73 | published them, feel free to delete them or edit them as much as you'd like. 74 | 75 | ## Usage 76 | 77 | ### Creating a Validation Ruleset 78 | 79 | #### Using the Generator Command 80 | 81 | This package comes with a command that you can use to quickly create a validation file to get you started right away. 82 | Lets say that you wanted to create a validation file for validating the config in the ``` config/app.php ``` file. To do 83 | this, you could use the following command: 84 | 85 | ```bash 86 | php artisan make:config-validation app 87 | ``` 88 | 89 | Running the above command would create a file in ``` config-validation/app.php ``` ready for you to start adding your config 90 | validation. 91 | 92 | #### Ruleset Location 93 | 94 | To validate your application's config, you need to define the validation rules first. You can do this by placing them inside 95 | files in a ``` config-validation ``` folder with names that match the config file you're validating. As an example, to 96 | validate the ``` config/app.php ``` config file, you would create a new file at ``` config-validation/app.php ``` that 97 | would hold the rules. 98 | 99 | #### Adding Rules to a Ruleset 100 | 101 | Once you have your ruleset file created in the ``` config-validation ``` folder, you can start adding your validation 102 | rules. 103 | 104 | Under the hood, Laravel Config Validator uses the built-in ``` Validator ``` class, so it should seem pretty familiar 105 | to work with. To check out the available Laravel validation rules that can be used, [click here](https://laravel.com/docs/8.x/validation#available-validation-rules). 106 | 107 | As an example, we might want to add a config validation rule to ensure that the ``` driver ``` field in the ``` app/mail.php ``` 108 | file is a supported field. To do this, we could create a file at ``` config-validation/mail.php ``` with the following: 109 | 110 | ```php 111 | rules(['in:smtp,sendmail,mailgun,ses,postmark,log,array']), 117 | // ... 118 | ]; 119 | ``` 120 | 121 | #### Custom Validation Error Messages 122 | 123 | There may be times when you want to override the error message for a specific validation rule. This can be done by passing 124 | in an array containing the messages to the ``` ->messages() ``` method for a ``` Rule ```. This array should follow the same 125 | pattern that would be used in a standard Laravel Validator object. 126 | 127 | As an example, we might want to add a config validation rule to ensure that the ``` driver ``` field in the ``` app/mail.php ``` 128 | file is a supported field and also use a custom error message. To do this, we could update our validation file to the following: 129 | 130 | ```php 131 | rules(['in:smtp,sendmail,mailgun,ses,postmark,log,array']) 138 | ->messages(['in' => 'The mail driver is invalid']), 139 | // ... 140 | ]; 141 | ``` 142 | 143 | #### Only Running in Specific App Environments 144 | 145 | You might not always want the same rule to be run in different environments. For example, you might want to have a relaxed 146 | set of validation rules for your local development environment and have a stricter set of rules for production. 147 | 148 | To explicitly specify the environment that a rule can be run in, you can use the ``` ->environments() ``` method. If no 149 | environment is defined, the rule will be run in all environments. 150 | 151 | The following example shows how you could set 2 different rules, one for production and one for local: 152 | 153 | ```php 154 | rules(['in:smtp,sendmail,mailgun,ses,postmark,log,array']) 161 | ->environments([Rule::ENV_LOCAL]), 162 | 163 | Rule::make('driver') 164 | ->rules(['in:mailgun']) 165 | ->environments([Rule::ENV_PRODUCTION]) 166 | ]; 167 | ``` 168 | 169 | ### Running the Validation 170 | 171 | #### Running the Validation Manually 172 | 173 | To run the config validation you can call the ``` ->run() ``` method on a ``` ConfigValidator ``` object. The example below 174 | shows how you could do this in a controller: 175 | 176 | ```php 177 | run(); 190 | 191 | return response()->json(['success' => true]); 192 | } 193 | } 194 | ``` 195 | 196 | ##### Only Running on Selected Config Files 197 | 198 | You might not always want to validate all of the config values in your application. So, you can specify the config files 199 | that you want to validate by passing the config names to the ``` ->run() ``` method as the first parameter. As an example, if you only wanted to validate 200 | the ``` auth.php ``` config file, you could use the following: 201 | 202 | ```php 203 | $configValidator = new ConfigValidator(); 204 | 205 | $configValidator->run(['auth']); 206 | ``` 207 | 208 | ##### Custom Folder Path 209 | 210 | If you aren't storing your validation files in the default ``` config/validation ``` folder, you can pass a custom folder path 211 | into the ``` ->run() ``` method as the second parameter. As an example, if you had the files stored in a ``` app/Custom/Validation ``` folder, you 212 | could use the following: 213 | 214 | ```php 215 | $configValidator = new ConfigValidator(); 216 | 217 | $configValidator->run([], 'app/Custom/Validation'); 218 | ``` 219 | 220 | ##### Running the Validator with Inline Rules 221 | 222 | There may be times when you want to run the validator with inline rules instead of using the rules defined in your config validation files. This can be useful if you want to run a one-off validation check, or validate the config values inside a package you maintain. 223 | 224 | To do this, you can use the `runInline` method like so: 225 | 226 | ```php 227 | use AshAllenDesign\ConfigValidator\Services\ConfigValidator; 228 | use AshAllenDesign\ConfigValidator\Services\Rule; 229 | 230 | $configValidator = new ConfigValidator(); 231 | 232 | $configValidator->runInline([ 233 | 'app' => [ 234 | Rule::make('env')->rules(['in:local,production']), 235 | Rule::make('debug')->rules(['boolean']), 236 | ], 237 | 'mail' => [ 238 | Rule::make('driver')->rules(['in:smtp,sendmail,mailgun,ses,postmark,log,array']), 239 | ], 240 | ]); 241 | ``` 242 | 243 | In the example above, we're running the validator with inline rules for the `app` and `mail` config files. The rules are the same as the ones we would define in the config validation files. 244 | 245 | The behaviour of the `runInline` method is the same as the `run` method. It will throw an exception if the validation fails, or return a boolean value if the `throwExceptionOnFailure` method has been set to `false`. 246 | 247 | #### Using the Command 248 | 249 | The library comes with a useful command that you can use to validate your config. To use it, you can run the following in 250 | the command line: 251 | 252 | ```bash 253 | php artisan config:validate 254 | ``` 255 | 256 | ##### Only Running on Selected Config Files (Command) 257 | 258 | You might not always want to validate all of the config values in your application. So, you can specify the config files 259 | that you want to validate in the command using the ``` --files ``` option. As an example, if you only wanted to validate 260 | the ``` auth.php ``` config file, you could use the following: 261 | 262 | ```bash 263 | php artisan config:validate --files=auth 264 | ``` 265 | 266 | As a further example, if you wanted to validate the ``` auth.php ``` and ``` app.php ``` files, you could use the following: 267 | 268 | ```bash 269 | php artisan config:validate --files=auth,app 270 | ``` 271 | 272 | ##### Custom Folder Path (Command) 273 | 274 | If you aren't storing your validation files in the default ``` config/validation ``` folder, you can pass a custom folder path 275 | into the ``` --path ``` option. As an example, if you had the files stored in a ``` app/Custom/Validation ``` folder, you 276 | could use the following: 277 | 278 | ```bash 279 | php artisan config:validate --path=app/Custom/Validation 280 | ``` 281 | 282 | #### Using a Service Provider 283 | 284 | You might want to run the config validator automatically on each request to ensure that you have the correct config. This 285 | can be particularly useful if you are in a local environment and switching between Git branches often. However, you might 286 | not want it to always run automatically in production for performance reasons. To run the validation automatically on each 287 | request, you can add it to the ``` boot ``` method of a service provider. 288 | 289 | The example below shows how to only run the validation in the local environment using the ``` AppServiceProvider ```: 290 | 291 | ```php 292 | run(); 306 | } 307 | } 308 | } 309 | 310 | ``` 311 | 312 | #### Throwing and Preventing Exceptions 313 | 314 | By default, the ` ConfigValidator ` will throw an ` InvalidConfigValueException ` exception if the validation fails. The exception will contain 315 | the error message of the first config value that failed the validation. You can prevent the exception from being thrown and instead 316 | rely on the boolean return value of the ` ->run() ` method by using the ` ->throwExceptionOnFailure() ` method. 317 | 318 | By preventing any exceptions from being thrown, it makes it easier for you to get all the failed validation errors using the 319 | ` ->errors() ` method. This will return the errors as an array. 320 | 321 | The example belows shows how you could prevent any exceptions from being thrown so that you can grab the errors: 322 | 323 | ```php 324 | $configValidator = new ConfigValidator(); 325 | 326 | $configValidator->throwExceptionOnFailure(false) 327 | ->run(); 328 | 329 | $errors = $configValidator->errors(); 330 | ``` 331 | 332 | ### Facade 333 | 334 | If you prefer to use facades in Laravel, you can choose to use the provided ``` ConfigValidator ``` facade instead of instantiating the ``` AshAllenDesign\ConfigValidator\Classes\ConfigValidator ``` 335 | class manually. 336 | 337 | The example below shows an example of how you could use the facade to run the config validation: 338 | 339 | ```php 340 | json(['success' => true]); 353 | } 354 | } 355 | ``` 356 | 357 | ## Security 358 | 359 | If you find any security related issues, please contact me directly at [mail@ashallendesign.co.uk](mailto:mail@ashallendesign.co.uk) to report it. 360 | 361 | ## Contribution 362 | 363 | If you wish to make any changes or improvements to the package, feel free to make a pull request. 364 | 365 | Note: A contribution guide will be added soon. 366 | 367 | ## Changelog 368 | 369 | Check the [CHANGELOG](CHANGELOG.md) to get more information about the latest changes. 370 | 371 | ## Upgrading 372 | 373 | Check the [UPGRADE](UPGRADE.md) guide to get more information on how to update this library to newer versions. 374 | 375 | ## License 376 | 377 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 378 | 379 | ## Support Me 380 | 381 | If you've found this package useful, please consider buying a copy of [Battle Ready Laravel](https://battle-ready-laravel.com) to support me and my work. 382 | 383 | Every sale makes a huge difference to me and allows me to spend more time working on open-source projects and tutorials. 384 | 385 | To say a huge thanks, you can use the code **BATTLE20** to get a 20% discount on the book. 386 | 387 | [👉 Get Your Copy!](https://battle-ready-laravel.com) 388 | 389 | [![Battle Ready Laravel](https://ashallendesign.co.uk/images/custom/sponsors/battle-ready-laravel-horizontal-banner.png)](https://battle-ready-laravel.com) 390 | --------------------------------------------------------------------------------