1: <?php
  2: 
  3: namespace Charcoal\Config;
  4: 
  5: use Traversable;
  6: use Throwable;
  7: use Exception;
  8: use LogicException;
  9: use InvalidArgumentException;
 10: use UnexpectedValueException;
 11: 
 12: 
 13: use Symfony\Component\Yaml\Parser as YamlParser;
 14: 
 15:  16:  17:  18:  19:  20:  21:  22:  23: 
 24: trait FileAwareTrait
 25: {
 26:      27:  28:  29:  30:  31:  32: 
 33:     public function loadFile($path)
 34:     {
 35:         if (!is_string($path)) {
 36:             throw new InvalidArgumentException(
 37:                 'File must be a string'
 38:             );
 39:         }
 40: 
 41:         if (!file_exists($path)) {
 42:             throw new InvalidArgumentException(
 43:                 sprintf('File "%s" does not exist', $path)
 44:             );
 45:         }
 46: 
 47:         $ext = pathinfo($path, PATHINFO_EXTENSION);
 48:         switch ($ext) {
 49:             case 'php':
 50:                 return $this->loadPhpFile($path);
 51: 
 52:             case 'json':
 53:                 return $this->loadJsonFile($path);
 54: 
 55:             case 'ini':
 56:                 return $this->loadIniFile($path);
 57: 
 58:             case 'yml':
 59:             case 'yaml':
 60:                 return $this->loadYamlFile($path);
 61:         }
 62: 
 63:         $validConfigExts = [ 'ini', 'json', 'php', 'yml' ];
 64:         throw new InvalidArgumentException(sprintf(
 65:             'Unsupported file format for "%s"; must be one of "%s"',
 66:             $path,
 67:             implode('", "', $validConfigExts)
 68:         ));
 69:     }
 70: 
 71:      72:  73:  74:  75:  76:  77: 
 78:     private function loadIniFile($path)
 79:     {
 80:         $data = parse_ini_file($path, true);
 81:         if ($data === false) {
 82:             throw new UnexpectedValueException(
 83:                 sprintf('INI file "%s" is empty or invalid', $path)
 84:             );
 85:         }
 86: 
 87:         return $data;
 88:     }
 89: 
 90:      91:  92:  93:  94:  95:  96:  97: 
 98:     private function loadJsonFile($path)
 99:     {
100:         $data = null;
101:         $json = file_get_contents($path);
102:         if ($json) {
103:             $data = json_decode($json, true);
104:             if (json_last_error() !== JSON_ERROR_NONE) {
105:                 $error = json_last_error_msg() ?: 'Unknown error';
106:                 throw new UnexpectedValueException(
107:                     sprintf('JSON file "%s" could not be parsed: %s', $path, $error)
108:                 );
109:             }
110:         }
111: 
112:         if (!is_array($data)) {
113:             return [];
114:         }
115: 
116:         return $data;
117:     }
118: 
119:     120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 
132:     private function loadPhpFile($path)
133:     {
134:         try {
135:             $data = include $path;
136:         } catch (Exception $e) {
137:             $message = sprintf('PHP file "%s" could not be parsed: %s', $path, $e->getMessage());
138:             throw new UnexpectedValueException($message, 0, $e);
139:         } catch (Throwable $e) {
140:             $message = sprintf('PHP file "%s" could not be parsed: %s', $path, $e->getMessage());
141:             throw new UnexpectedValueException($message, 0, $e);
142:         }
143: 
144:         if (is_array($data) || ($data instanceof Traversable)) {
145:             return $data;
146:         }
147: 
148:         return [];
149:     }
150: 
151:     152: 153: 154: 155: 156: 157: 158: 159: 
160:     private function loadYamlFile($path)
161:     {
162:         if (!class_exists('Symfony\Component\Yaml\Parser')) {
163:             throw new LogicException('YAML format requires the Symfony YAML component');
164:         }
165: 
166:         try {
167:             $yaml = new YamlParser();
168:             $data = $yaml->parseFile($path);
169:         } catch (Exception $e) {
170:             $message = sprintf('YAML file "%s" could not be parsed: %s', $path, $e->getMessage());
171:             throw new UnexpectedValueException($message, 0, $e);
172:         }
173: 
174:         if (!is_array($data)) {
175:             return [];
176:         }
177: 
178:         return $data;
179:     }
180: }
181: