-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathclass-array-config-writer.php
459 lines (401 loc) · 12.9 KB
/
class-array-config-writer.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
<?php
/*
Copyright 2016 Wakeel Ogunsanya
Licensed under GPLv2 or above
Version 1.2.2
*/
class Array_Config_Writer {
/**
* Skip updating the index if the index acctually exist
*
* This will also create new index if not exist
*/
const SKIP_IF_EXIST = 0;
/**
* Skip creating new index if the specified index is not found
*/
const SKIP_CREATE_IF_NOT_EXIST = 1;
/**
* Update or create the index
*/
const CREATE_OR_UPDATE = 2;
/**
* The config source file
*
* set during with class construct
* @var string
*/
protected $_file ;
/**
* The config file to write
*
* Default to source file
* @var string
*/
protected $_destinationFile ;
/**
* Content read from file
* @var string
*/
protected $_fileContent;
/**
* The variable to search for in the file
*
* eg if we can to update 'option_name' value in a file with
* $config['option_name'] array, the variable is 'config'
*
* @note we assume each option index is on seperate line like
* $config['option_name_one'] = 'one' ;
* $config['option_name_two'] = 'two' ;
*
* if you have array like $config['options'] = array(
* 'option_name_one' => 'one',
* 'option_name_two' => 'two'
* );
* You have to first get $config['options'] in variable then manually update
* the elements, after which you will can use this config writer to update the
* $config['options'] element
*
* @var string
*/
protected $_variable ;
/**
* The target index of the variable
*
* @var string
*/
protected $_index ;
/**
*
* @var string
*/
protected $_replacement;
/**
*
* @var string
*/
protected $_lastError;
/**
* This option determines whether the file should be updated automatically
*
* if set to false, you will manually call save() method after you
* have done your writing with write() method
*
* @since 1.1.0
* @var boolean
*/
protected $_autoSave = true ;
/**
*
* @param string $config_file Asolute path to config file
* @param string $variable_name the name of the config varible to update
*/
public function __construct($config_file , $variable_name = '\$config' , $auto_save = true )
{
$this->_file = $config_file ;
$this->_destinationFile = $config_file ;
$this->_autoSave = $auto_save ;
$this->setVariableName($variable_name );
if ( ! file_exists($this->_file))
{
//throw new Exception('Config Write Error: Config file doesnt exists ' . $this->_file);
$this->_lastError = 'Config Write Error: Config file doesnt exists ' . $this->_file ;
return ;
}
if ( ! $variable_name)
{
$this->_lastError = 'You must set the set parameter of the library construct has varible to update' ;
return ;
}
$this->_fileContent = file_get_contents( $this->_file ) ;
}
/**
* Wite or update an item of the config array
*
* @param string|array $index To update 'language' index $config['language']
* This will be string 'language'
* To update hostname' of $db['default']['hostname'] , then index
* will be array( 'default' , 'hostname' )
*
* @param mixed $replacement
* @param boolean $write_method Skip updating item if already exists
* @param null|array $comments Comment to add to the top of item (new item), each element
* will be placed oon a new line. * is added before each line , meaning
* you dont have to put /** or * unless you want it show
* @throws Exception for invalid write method
*
* @note you can not update existing item comment
*
*
* @return boolean
*
*/
public function write( $index = null, $replacement = null , $write_method = self::CREATE_OR_UPDATE , $comments = null )
{
// error exists in the constructor?
if($this->_lastError)
{
return $this;
}
$this->_fileContent = trim($this->_fileContent);
if(!$index)
{
$this->_lastError = 'You must set index you want to update' ;
return $this;
}
$prefix = $this->_variable ;
$regex = '#(' . $prefix . ')(';
// add a mark in case config item doesnt exists
$mark = "{$prefix}" ;
// we can update multi dimentional
$indices = is_array($index)? $index : array( $index ) ;
$comment_str = '' ;
foreach ( $indices as $i)
{
$is_int = is_int($i) ;
// we make sure we dont change the index type if its numeric
$new_item_index = $is_int? $i : "'$i'" ;
$regex .= '\[\s*';
// if the index is int, we dont need ' or "" to be checked in the regex
$regex .= $is_int? '' : '(\'|\")' ;
$regex .= '('.$i.')*';
$regex .= $is_int? '' : '(\'|\")';
$regex .= '\s*\]' ;
// Used before we seperated numeric index from string
//$regex .= '\[\s*(\'|\")(' . $i . ')*(\'|\")\s*\]' ;
//placed in file as new index if doesn't exist
$mark .= "[$new_item_index]" ;
}
// value
$regex .= ')\s*=\s?' ;
$regex .= "({$this->getStringRegex()}|{$this->getIntRegex()}|{$this->getArrayRegex()})" ;
//allow ; within a string @issue 7
// $regex .= '|.*".*".*|' ;
// $regex .= "|.*'.*'.*|";
//close value match
$regex .= ';\s*$#m' ;
$mark .= " = ";
$exists = preg_match($regex, $this->_fileContent, $m);
if($exists)
{
// well config aleady exists
// may be is application upgrade :) we wouldnt wana overide user settings
if( $write_method == self::SKIP_IF_EXIST )
{
return $this;
}
// update the content
$this->_fileContent = preg_replace( $regex ,
'$1$2 = ' . var_export( $replacement , true ).';' , $this->_fileContent ) ;
}
// config item doesnt exist yet create new index if reqyuired
else
{
switch ($write_method)
{
case self::SKIP_CREATE_IF_NOT_EXIST:
return $this;
case self::CREATE_OR_UPDATE:
case self::SKIP_IF_EXIST:
// new item here
$mark .= var_export( $replacement , true ) . ' ;' ;
$mark .= "\n" ;
$mark .= "\n" ;
// add comment if provided
if(is_array($comments) && count($comments) > 0 )
{
//open comment
$comment_str .= '/**' ;
$comment_str .= "\n" ;
foreach ($comments as $line)
{
$comment_str .= ' * ' . $line . "\n" ;
}
// close the comment
$comment_str .= '*/';
}
// lets try remove traling slash from the variable name since
// we are writing php here
if ( substr($mark, 0 , 1) == '\\' )
{
$mark = substr($mark, 1 );
}
// we have updated the index, the file will save automatically in the class shutdwon
// we did this allow update multiple index by call write() method as manay times
// as required
//
// check if the file has php closing tag
if(substr($this->_fileContent, -2) === '?>' )
{
// remove the closing tag before adding our new item
$this->_fileContent = substr($this->_fileContent, 0 , -2). "\n" . $comment_str . "\n" . $mark . ' ?>' ;
}
else
{
$this->_fileContent = $this->_fileContent . "\n" . $comment_str . "\n" . $mark . "\n" ;
}
break;
case self::SKIP_CREATE_IF_NOT_EXIST:
return $this;
default :
throw new Exception('Class: '.__CLASS__ .' Method :'.__METHOD__.'third parameter is invalid. Use the class constant');
}
}
return $this;
}
/**
* Update the file name of the array config
*
* You will olnly do this to transfer the php file content to another file
*
* @param string $name
* @deprecated since 1.3.0 use setDestinationFile() method
*
* @return Array_Config_Writer for method chaining
*/
public function setFilename($name = null)
{
return $this->setDestinationFile($name);
}
/**
* Set the file to save updated configuration
* @since 1.3.0
*
* @param string $name
*
* @return Array_Config_Writer for method chaining
*/
public function setDestinationFile($path)
{
$this->_destinationFile = $path;
return $this;
}
/**
*
* @return string
*/
public function getDestinationFile()
{
return $this->_destinationFile;
}
/**
* Set the config varible name
* // actually we expect someting like '\$config' but user migt just provide 'config'
*
* @param string $name
* @since 1.3.0
* @return Array_Config_Writer
*/
public function setVariableName($name = null)
{
if( ! is_string($name))
{
$this->_lastError = 'Variable name not string: '. $name;
return $this;
}
if(substr($name, 0, 1 ) != '$' && substr($name, 1, 1 ) != '$')
{
$name = '$' . $name ;
}
if(substr($name, 0 , 1 ) != '\\')
{
$name = '\\' . $name ;
}
$this->_variable = $name;
return $this;
}
/**
* @return string
*/
public function getVariableName()
{
return $this->_variable;
}
/**
* We can now update the file content
*
* if the _autosave property is true, we auto update file
*
*/
public function __destruct()
{
if($this->_autoSave)
{
$this->save();
}
}
/**
* Save the new content to file
*
* You can call Array_Config_Writer::write() as many times as required
* before calling this method
*
* @return \Array_Config_Writer
*/
public function save()
{
if( ! $this->_lastError)
{
file_put_contents( $this->_destinationFile , $this->_fileContent ) ;
}
return $this;
}
/**
* Check if any error has occured
*
* @return boolean
*/
public function hasError()
{
return ! empty($this->_lastError);
}
/**
* Get last error
*
* @return string Last error by the library if any
*/
public function getLastError()
{
return $this->_lastError;
}
/**
* Set auto save option
*
* @since 1.1.1
* @param boolean $option
*/
public function setAutoSave($option = true)
{
$this->_autoSave = $option;
return $this;
}
/**
* Set auto save option
* @since 1.3.0
*
* @return boolean Auto save option
*/
public function getAutoSave()
{
return $this->_autoSave;
}
/**
* Get confil file content
*/
public function getContent()
{
return $this->_fileContent;
}
protected function getStringRegex()
{
return '(\'|").*(\'|")';
}
protected function getIntRegex()
{
return '[^;]+';
}
protected function getArrayRegex()
{
return '(:?array|\[)\s*\(.*(:?\)|\])*';
}
}