Passed
Push — master ( 51995c...0bccda )
by Paul
06:07 queued 02:50
created

ConfigManager::normalizeYamlValues()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 8
rs 9.4285
cc 2
eloc 6
nc 1
nop 1
1
<?php
2
3
namespace GeminiLabs\Pollux\Config;
4
5
use GeminiLabs\Pollux\Application;
6
use GeminiLabs\Pollux\Config\Config;
7
use GeminiLabs\Pollux\MetaBox\SiteMetaManager;
8
use Symfony\Component\Yaml\Exception\DumpException;
9
use Symfony\Component\Yaml\Exception\ParseException;
10
use Symfony\Component\Yaml\Yaml;
11
12
/**
13
 * @property int $updated
14
 */
15
class ConfigManager extends SiteMetaManager
16
{
17
	const RAW_STRINGS = [
18
		'__', '_n', '_x', 'sprintf',
19
	];
20
21
	/**
22
	 * @var Application
23
	 */
24
	protected $app;
25
26
	public function __construct( Application $app )
27
	{
28
		$this->app = $app;
29
		$this->options = $this->buildConfig();
30
	}
31
32
	/**
33
	 * @param string $group
34
	 * @return object|array|null
35
	 */
36
	public function __get( $group )
37
	{
38
		if( $group == 'yaml' ) {
39
			return $this->yaml();
40
		}
41
		return parent::__get( $group );
42
	}
43
44
	/**
45
	 * @return array
46
	 */
47
	public function buildConfig()
48
	{
49
		$yamlFile = $this->getYamlFile();
50
		$yaml = $this->normalize(
51
			$this->parseYaml( file_get_contents( $yamlFile ))
52
		);
53
		if( !$yaml['disable_config'] ) {
54
			$config = $this->normalizeArray(
55
				array_filter( (array) get_option( Config::id(), [] ))
56
			);
57
		}
58
		return empty( $config )
59
			? $this->setTimestamp( $yaml, filemtime( $yamlFile ))
60
			: $this->normalize( $config );
61
	}
62
63
	/**
64
	 * @return object
65
	 */
66
	public function compile()
67
	{
68
		$configFile = $this->getCompileDestination();
69
		if( $this->shouldCompile( $configFile )) {
70
			file_put_contents( $configFile, sprintf( '<?php // DO NOT MODIFY THIS FILE DIRECTLY!%sreturn (object) %s;',
71
				PHP_EOL,
72
				$this->parseRawStrings( var_export( $this->setTimestamp( $this->options ), true ))
73
			));
74
		}
75
		return include $configFile;
76
	}
77
78
	/**
79
	 * @return string
80
	 */
81
	public function convertArrayToYaml( array $array )
82
	{
83
		return !empty( $array )
84
			? trim( $this->parseRawStrings( $this->dumpYaml( $array )))
85
			: '';
86
	}
87
88
	/**
89
	 * @return string
90
	 */
91
	public function getCompileDestination( $filename = 'pollux-config.php' )
92
	{
93
		$filename = apply_filters( 'pollux/config/dist/file', $filename );
94
		$storagePath = apply_filters( 'pollux/config/dist/location', WP_CONTENT_DIR );
95
		wp_mkdir_p( $storagePath );
96
		return sprintf( '%s%s', trailingslashit( $storagePath ), $filename );
97
	}
98
99
	/**
100
	 * @return string
101
	 */
102
	public function getYamlFile()
103
	{
104
		$theme = wp_get_theme();
105
		$configYaml = apply_filters( 'pollux/config/src/file', 'pollux.yml' );
106
		$configLocations = apply_filters( 'pollux/config/src/location', [
107
			trailingslashit( trailingslashit( $theme->theme_root ) . $theme->stylesheet ),
108
			trailingslashit( trailingslashit( $theme->theme_root ) . $theme->template ),
109
			trailingslashit( WP_CONTENT_DIR ),
110
			trailingslashit( ABSPATH ),
111
			trailingslashit( dirname( ABSPATH )),
112
			trailingslashit( dirname( dirname( ABSPATH ))),
113
		]);
114
		foreach( (array) $configLocations as $location ) {
115
			if( !file_exists( $location . $configYaml ))continue;
116
			return $location . $configYaml;
117
		}
118
		return $this->app->path( 'defaults.yml' );
119
	}
120
121
	/**
122
	 * @return array
123
	 */
124
	public function normalizeArray( array $array )
125
	{
126
		return array_map( function( $value ) {
127
			return !is_numeric( $value ) && is_string( $value )
128
				? $this->parseYaml( $value )
129
				: $value;
130
		}, $array );
131
	}
132
133
	/**
134
	 * @return array
135
	 */
136
	public function normalizeYamlValues( array $array )
137
	{
138
		return array_map( function( $value ) {
139
			return is_array( $value )
140
				? $this->convertArrayToYaml( $value )
141
				: $value;
142
		}, $array );
143
	}
144
145
	/**
146
	 * @return array
147
	 */
148
	public function setTimestamp( array $config, $timestamp = null )
149
	{
150
		$timestamp || $timestamp = time();
151
		$config['updated'] = $timestamp;
152
		return $config;
153
	}
154
155
	/**
156
	 * @return object
157
	 */
158
	public function yaml()
159
	{
160
		return (object) $this->normalizeYamlValues( $this->options );
161
	}
162
163
	/**
164
	 * @return string|null
165
	 */
166
	protected function dumpYaml( array $array )
167
	{
168
		try {
169
			return Yaml::dump( $array, 13, 2 );
170
		}
171
		catch( DumpException $e ) {
172
			error_log( print_r( $e->getMessage(), 1 ));
173
		}
174
	}
175
176
	/**
177
	 * @return array
178
	 */
179
	protected function normalize( array $config )
180
	{
181
		return wp_parse_args(
182
			$config,
183
			$this->parseYaml( file_get_contents( $this->app->path( 'defaults.yml' )))
184
		);
185
	}
186
187
	/**
188
	 * @param string $configString
189
	 * @return string
190
	 */
191
	protected function parseRawStrings( $configString )
192
	{
193
		$strings = apply_filters( 'pollux/config/raw_strings', static::RAW_STRINGS );
194
		$pattern = '/(\')((' . implode( '|', $strings ) . ')\(?.+\))(\')/';
195
		return stripslashes(
196
			preg_replace_callback( $pattern, function( $matches ) {
197
				return str_replace( "''", "'", $matches[2] );
198
			}, $configString )
199
		);
200
	}
201
202
	/**
203
	 * @return mixed
204
	 */
205
	protected function parseYaml( $value )
206
	{
207
		try {
208
			return Yaml::parse( $value );
209
		}
210
		catch( ParseException $e ) {
211
			// http://api.symfony.com/3.2/Symfony/Component/Yaml/Exception/ParseException.html
212
			error_log( print_r( sprintf( 'Unable to parse the YAML string: %s', $e->getMessage() ), 1 ));
213
			error_log( print_r( $e->getParsedFile(), 1 ));
214
			error_log( print_r( $e->getParsedLine(), 1 ));
215
			error_log( print_r( $e->getSnippet(), 1 ));
216
		}
217
	}
218
219
	/**
220
	 * @param string $configFile
221
	 * @return bool
222
	 */
223
	protected function shouldCompile( $configFile )
224
	{
225
		if( !file_exists( $configFile )) {
226
			return true;
227
		}
228
		$config = include $configFile;
229
		if( $this->updated >= $config->updated ) {
230
			return true;
231
		}
232
		return filemtime( $this->getYamlFile() ) >= $config->updated;
233
	}
234
}
235