Created
September 3, 2014 19:13
-
-
Save rjackson/70e4a7c059d0a8916ef1 to your computer and use it in GitHub Desktop.
Updated PHP VDF code to support arrays of maps.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
error_reporting(-1); | |
ini_set('display_errors', 'On'); | |
$test_data = <<<TEST_DATA | |
"treasure_chest_59_trappers_pelt" | |
{ | |
"Crescent" "1" | |
"Rightful Heir" "1" | |
"Tribal Terror" "1" | |
"Vestments of the Infinite Waves" "1" | |
"additional_drop" | |
{ | |
"chance" "1" | |
"item" "The Iron Claw" | |
} | |
"additional_drop" | |
{ | |
"chance" "1" | |
"item" "Jin and Yin Fox Spirits" | |
} | |
"additional_drop" | |
{ | |
"chance" "1" | |
"loot_list" "treasure_chest_58_59_unusual_courier" | |
} | |
} | |
TEST_DATA; | |
include("vdf.php"); | |
$kv_data = new KeyValues("DATER"); | |
header("content-type: application/json"); | |
echo json_encode($kv_data->load($test_data)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Tree-based Valve KeyValues parser | |
* Updated 2014-09-03 by Rob Jackson to support arrays of maps. | |
* | |
* @author GameConnect | |
* @author Rob Jackson <[email protected]> | |
* | |
* @copyright (C)2007-2013 GameConnect.net. All rights reserved. | |
* @link http://www.sourcebans.net | |
* | |
* @package sourcebans.components | |
* @since 2.0 | |
*/ | |
class KeyValues | |
{ | |
/** | |
* @var string Root section name | |
*/ | |
protected $_name; | |
/** | |
* Constructor | |
* | |
* @param string $name Root section name | |
* @param array $data Optional key values data | |
*/ | |
public function __construct($name = null, $data = null) | |
{ | |
$this->_name = $name; | |
if(is_array($data)) | |
{ | |
parent::__construct($data); | |
} | |
} | |
/** | |
* Returns a property value | |
* | |
* @param string $name The property name | |
* @return mixed The property value | |
*/ | |
public function __get($name) | |
{ | |
switch($name) | |
{ | |
case 'name': | |
return $this->_name; | |
} | |
} | |
/** | |
* Blocks setting property values | |
* | |
* @param string $name The property name | |
* @param string $value The property value | |
*/ | |
public function __set($name, $value) {} | |
/** | |
* Returns a string representation of the data | |
* | |
* @return string The string representation of the data | |
*/ | |
public function __toString() | |
{ | |
$ret = $this->name . "\n{\n"; | |
$ret .= $this->_build(parent::getArrayCopy()); | |
$ret .= '}'; | |
return $ret; | |
} | |
/** | |
* Loads key values data from a string or file | |
* | |
* @param string $string String or file to load | |
*/ | |
public function load($string) | |
{ | |
if(is_readable($string)) | |
{ | |
$string = file_get_contents($string); | |
} | |
// Use token_get_all() to easily ignore comments and whitespace | |
$tokens = token_get_all("<?php\n" . $string . "\n?>"); | |
$data = $this->_parse($tokens); | |
// Strip root section | |
$data = reset($data); | |
return $data; | |
} | |
/** | |
* Saves key values data to file | |
* | |
* @param string $file File to save to | |
* @return boolean Whether saving was successful | |
*/ | |
public function save($file) | |
{ | |
if(($fp = fopen($file, 'w+')) === false) | |
return false; | |
fwrite($fp, $this); | |
fclose($fp); | |
return true; | |
} | |
/** | |
* Serializes key values data and root section name | |
*/ | |
public function serialize() | |
{ | |
return serialize(array( | |
'data' => parent::serialize(), | |
'name' => $this->_name, | |
)); | |
} | |
/** | |
* Returns an array representation of the data | |
* | |
* @return array The array representation of the data | |
*/ | |
public function toArray() | |
{ | |
return parent::getArrayCopy(); | |
} | |
/** | |
* Unserializes key values data and root section name | |
*/ | |
public function unserialize($data) | |
{ | |
$data = unserialize($data); | |
$this->_name = $data['name']; | |
parent::unserialize($data['data']); | |
} | |
/** | |
* Recursively builds key values string | |
* | |
* @param array $data Key values data to write | |
* @param integer $level Optional level to use for indenting | |
*/ | |
private function _build($data, $level = null) | |
{ | |
// Default level to 0 | |
if(!is_numeric($level)) | |
{ | |
$level = 0; | |
} | |
$indent = str_repeat("\t", $level + 1); | |
$ret = ''; | |
foreach($data as $key => $value) | |
{ | |
if(is_array($value)) | |
{ | |
reset($value); | |
// If array is numerical, write key sub-value pairs | |
if(is_int(key($value))) | |
{ | |
foreach($value as $sub_value) | |
{ | |
$ret .= sprintf("%s\"%s\"\t\"%s\"\n", | |
$indent, $key, $sub_value); | |
} | |
} | |
// Otherwise, recursively write section | |
else | |
{ | |
$ret .= sprintf("%s\"%s\"\n%s{\n", | |
$indent, $key, $indent); | |
$ret .= $this->_build($value, $level + 1); | |
$ret .= $indent . "}\n"; | |
} | |
} | |
// Write key value pair | |
else | |
{ | |
$ret .= sprintf("%s\"%s\"\t\"%s\"\n", | |
$indent, $key, $value); | |
} | |
} | |
return $ret; | |
} | |
// Returns whether the given array is associative (has keys) or sequential. | |
private function is_assoc($array) { | |
return (bool)count(array_filter(array_keys($array), 'is_string')); | |
} | |
/** | |
* Recursively parses key values data from tokens | |
* | |
* @param array $tokens Tokens received from token_get_all() | |
*/ | |
private function _parse(&$tokens) | |
{ | |
$data = array(); | |
$key = null; | |
// Use each() so the array cursor is also advanced | |
// when the function is called recursively | |
while(list(, $token) = each($tokens)) | |
{ | |
// New section | |
if($token == '{') | |
{ | |
// Recursively parse section | |
if (array_key_exists($key, $data)) { | |
// If key is already in data then we have an array of values | |
if (!is_array($data[$key]) || $this->is_assoc($data[$key])) { | |
// If the existing data is not a sequential array we need to convert it to an array. | |
$data[$key] = array($data[$key]); | |
} | |
// Append data to array | |
$data[$key][] = $this->_parse($tokens); | |
} | |
else { | |
// If the key isn't in the data, add it! | |
$data[$key] = $this->_parse($tokens); | |
} | |
$key = null; | |
} | |
// End section | |
else if($token == '}') | |
{ | |
return $data; | |
} | |
// Key or value | |
else | |
{ | |
$value = $token[1]; | |
switch($token[0]) | |
{ | |
case T_CONSTANT_ENCAPSED_STRING: | |
// Strip surrounding quotes, then parse as a string | |
$value = substr($value, 1, -1); | |
case T_STRING: | |
// If key is not set, store | |
if(is_null($key)) | |
{ | |
$key = $value; | |
} | |
// Otherwise, it's a key value pair | |
else | |
{ | |
// If value is already set, treat as an array | |
// to allow multiple values per key | |
if(isset($data[$key])) | |
{ | |
// If value is not an array, cast | |
if(!is_array($data[$key])) | |
{ | |
$data[$key] = (array)$data[$key]; | |
} | |
// Add value to array | |
$data[$key][] = $value; | |
} | |
// Otherwise, store key value pair | |
else | |
{ | |
$data[$key] = $value; | |
} | |
$key = null; | |
} | |
} | |
} | |
} | |
return $data; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment