Created
November 1, 2013 17:28
-
-
Save lokielse/7268829 to your computer and use it in GitHub Desktop.
php convert array to plist
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 | |
/** | |
* PropertyList class | |
* Implements writing Apple Property List (.plist) XML and text files from an array. | |
* | |
* @author Jesus A. Alvarez <zydeco@namedfork.net> | |
*/ | |
//====================================================demo | |
$json = '{"安徽":["合肥","安庆","蚌埠","亳州","巢湖","滁州","阜阳","贵池","淮北","淮化","淮南","黄山","九华山","六安","马鞍山","宿州","铜陵","屯溪","芜湖","宣城"],"北京":["东城","西城","崇文","宣武","朝阳","丰台","石景山","海淀","门头沟","房山","通州","顺义","昌平","大兴","平谷","怀柔","密云","延庆"],"重庆":["万州","涪陵","渝中","大渡口","江北","沙坪坝","九龙坡","南岸","北碚","万盛","双挢","渝北","巴南","黔江","长寿","綦江","潼南","铜梁","大足","荣昌","壁山","梁平","城口","丰都","垫江","武隆","忠县","开县","云阳","奉节","巫山","巫溪","石柱","秀山","酉阳","彭水","江津","合川","永川","南川"],"福建":["福州","福安","龙岩","南平","宁德","莆田","泉州","三明","邵武","石狮","晋江","永安","武夷山","厦门","漳州"],"甘肃":["兰州","白银","定西","敦煌","甘南","金昌","酒泉","临夏","平凉","天水","武都","武威","西峰","嘉峪关","张掖"],"广东":["广州","潮阳","潮州","澄海","东莞","佛山","河源","惠州","江门","揭阳","开平","茂名","梅州","清远","汕头","汕尾","韶关","深圳","顺德","阳江","英德","云浮","增城","湛江","肇庆","中山","珠海"],"广西":["南宁","百色","北海","桂林","防城港","河池","贺州","柳州","来宾","钦州","梧州","贵港","玉林"],"贵州":["贵阳","安顺","毕节","都匀","凯里","六盘水","铜仁","兴义","玉屏","遵义"],"海南":["海口","三亚","五指山","琼海","儋州","文昌","万宁","东方","定安","屯昌","澄迈","临高","万宁","白沙黎族","昌江黎族","乐东黎族","陵水黎族","保亭黎族","琼中黎族","西沙群岛","南沙群岛","中沙群岛"],"河北":["石家庄","保定","北戴河","沧州","承德","丰润","邯郸","衡水","廊坊","南戴河","秦皇岛","唐山","新城","邢台","张家口"],"黑龙江":["哈尔滨","北安","大庆","大兴安岭","鹤岗","黑河","佳木斯","鸡西","牡丹江","齐齐哈尔","七台河","双鸭山","绥化","伊春"],"河南":["郑州","安阳","鹤壁","潢川","焦作","济源","开封","漯河","洛阳","南阳","平顶山","濮阳","三门峡","商丘","新乡","信阳","许昌","周口","驻马店"],"香港":["香港","九龙","新界"],"湖北":["武汉","恩施","鄂州","黄冈","黄石","荆门","荆州","潜江","十堰","随州","武穴","仙桃","咸宁","襄阳","襄樊","孝感","宜昌"],"湖南":["长沙","常德","郴州","衡阳","怀化","吉首","娄底","邵阳","湘潭","益阳","岳阳","永州","张家界","株洲"],"江苏":["南京","常熟","常州","海门","淮安","江都","江阴","昆山","连云港","南通","启东","沭阳","宿迁","苏州","太仓","泰州","同里","无锡","徐州","盐城","扬州","宜兴","仪征","张家港","镇江","周庄"],"江西":["南昌","抚州","赣州","吉安","景德镇","井冈山","九江","庐山","萍乡","上饶","新余","宜春","鹰潭"],"吉林":["长春","白城","白山","珲春","辽源","梅河","吉林","四平","松原","通化","延吉"],"辽宁":["沈阳","鞍山","本溪","朝阳","大连","丹东","抚顺","阜新","葫芦岛","锦州","辽阳","盘锦","铁岭","营口"],"澳门":["澳门"],"内蒙古":["阿拉善盟","包头","赤峰","东胜","海拉尔","集宁","临河","通辽","乌海","乌兰浩特","锡林浩特"],"宁夏":["银川","固原","中卫","石嘴山","吴忠"],"青海":["西宁","德令哈","格尔木","共和","海东","海晏","玛沁","同仁","玉树"],"山东":["济南","滨州","兖州","德州","东营","菏泽","济宁","莱芜","聊城","临沂","蓬莱","青岛","曲阜","日照","泰安","潍坊","威海","烟台","枣庄","淄博"],"上海":["崇明","黄浦","卢湾","徐汇","长宁","静安","普陀","闸北","虹口","杨浦","闵行","宝山","嘉定","浦东","金山","松江","青浦","南汇","奉贤","朱家角"],"山西":["太原","长治","大同","候马","晋城","离石","临汾","宁武","朔州","忻州","阳泉","榆次","运城"],"陕西":["西安","安康","宝鸡","汉中","渭南","商州","绥德","铜川","咸阳","延安","榆林"],"四川":["成都","巴中","达州","德阳","都江堰","峨眉山","涪陵","广安","广元","九寨沟","康定","乐山","泸州","马尔康","绵阳","眉山","南充","内江","攀枝花","遂宁","汶川","西昌","雅安","宜宾","自贡","资阳"],"台湾":["台北","基隆","台南","台中","高雄","屏东","南投","云林","新竹","彰化","苗栗","嘉义","花莲","桃园","宜兰","台东","金门","马祖","澎湖","其它"],"天津":["天津","和平","东丽","河东","西青","河西","津南","南开","北辰","河北","武清","红挢","塘沽","汉沽","大港","宁河","静海","宝坻","蓟县"],"新疆":["阿克苏","阿勒泰","阿图什","博乐","昌吉","东山","哈密","和田","喀什","克拉玛依","库车","库尔勒","奎屯","石河子","塔城","吐鲁番","伊宁"],"西藏":["拉萨","阿里","昌都","林芝","那曲","日喀则","山南"],"云南":["昆明","大理","保山","楚雄","大理","东川","个旧","景洪","开远","临沧","丽江","六库","潞西","曲靖","思茅","文山","西双版纳","玉溪","中甸","昭通"],"浙江":["杭州","安吉","慈溪","定海","奉化","海盐","黄岩","湖州","嘉兴","金华","临安","临海","丽水","宁波","瓯海","平湖","千岛湖","衢州","江山","瑞安","绍兴","嵊州","台州","温岭","温州","余姚","舟山"],"海外":["美国","英国","法国","瑞士","澳洲","新西兰","加拿大","奥地利","韩国","日本","德国","意大利","西班牙","俄罗斯","泰国","印度","荷兰","新加坡","欧洲","北美","南美","亚洲","非洲","大洋洲"]}'; | |
$obj = (array)json_decode($json); | |
$plist = plist_encode_xml($obj); | |
print_r($plist) ; | |
//====================================================demo | |
function plist_encode_text ($obj) { | |
$plist = new PropertyList($obj); | |
return $plist->text(); | |
} | |
function plist_encode_xml ($obj) { | |
$plist = new PropertyList($obj); | |
return $plist->xml(); | |
} | |
class PropertyList | |
{ | |
private $obj, $xml, $text; | |
public function __construct ($obj) { | |
$this->obj = $obj; | |
} | |
private static function is_assoc ($array) { | |
return (is_array($array) && 0 !== count(array_diff_key($array, array_keys(array_keys($array))))); | |
} | |
public function xml () { | |
if (isset($this->xml)) return $this->xml; | |
$x = new XMLWriter(); | |
$x->openMemory(); | |
$x->setIndent(TRUE); | |
$x->startDocument('1.0', 'UTF-8'); | |
$x->writeDTD('plist', '-//Apple//DTD PLIST 1.0//EN', 'http://www.apple.com/DTDs/PropertyList-1.0.dtd'); | |
$x->startElement('plist'); | |
$x->writeAttribute('version', '1.0'); | |
$this->xmlWriteValue($x, $this->obj); | |
$x->endElement(); // plist | |
$x->endDocument(); | |
$this->xml = $x->outputMemory(); | |
return $this->xml; | |
} | |
public function text() { | |
if (isset($this->text)) return $this->text; | |
$text = ''; | |
$this->textWriteValue($text, $this->obj); | |
$this->text = $text; | |
return $this->text; | |
} | |
private function xmlWriteDict(XMLWriter $x, &$dict) { | |
$x->startElement('dict'); | |
foreach($dict as $k => &$v) { | |
$x->writeElement('key', $k); | |
$this->xmlWriteValue($x, $v); | |
} | |
$x->endElement(); // dict | |
} | |
private function xmlWriteArray(XMLWriter $x, &$arr) { | |
$x->startElement('array'); | |
foreach($arr as &$v) | |
$this->xmlWriteValue($x, $v); | |
$x->endElement(); // array | |
} | |
private function xmlWriteValue(XMLWriter $x, &$v) { | |
if (is_int($v) || is_long($v)) | |
$x->writeElement('integer', $v); | |
elseif (is_float($v) || is_real($v) || is_double($v)) | |
$x->writeElement('real', $v); | |
elseif (is_string($v)) | |
$x->writeElement('string', $v); | |
elseif (is_bool($v)) | |
$x->writeElement($v?'true':'false'); | |
elseif (PropertyList::is_assoc($v)) | |
$this->xmlWriteDict($x, $v); | |
elseif (is_array($v)) | |
$this->xmlWriteArray($x, $v); | |
elseif (is_a($v, 'PlistData')) | |
$x->writeElement('data', $v->base64EncodedData()); | |
elseif (is_a($v, 'PlistDate')) | |
$x->writeElement('date', $v->encodedDate()); | |
else { | |
trigger_error("Unsupported data type in plist ($v)", E_USER_WARNING); | |
$x->writeElement('string', $v); | |
} | |
} | |
private function textWriteValue(&$text, &$v, $indentLevel = 0) { | |
if (is_int($v) || is_long($v)) | |
$text .= sprintf("%d", $v); | |
elseif (is_float($v) || is_real($v) || is_double($v)) | |
$text .= sprintf("%g", $v); | |
elseif (is_string($v)) | |
$this->textWriteString($text, $v); | |
elseif (is_bool($v)) | |
$text .= $v?'YES':'NO'; | |
elseif (PropertyList::is_assoc($v)) | |
$this->textWriteDict($text, $v, $indentLevel); | |
elseif (is_array($v)) | |
$this->textWriteArray($text, $v, $indentLevel); | |
elseif (is_a($v, 'PlistData')) | |
$text .= '<' . $v->hexEncodedData() . '>'; | |
elseif (is_a($v, 'PlistDate')) | |
$text .= '"' . $v->ISO8601Date() . '"'; | |
else { | |
trigger_error("Unsupported data type in plist ($v)", E_USER_WARNING); | |
$this->textWriteString($text, $v); | |
} | |
} | |
private function textWriteString(&$text, &$str) { | |
$oldlocale = setlocale(LC_CTYPE, "0"); | |
if (ctype_alnum($str)) $text .= $str; | |
else $text .= '"' . $this->textEncodeString($str) . '"'; | |
setlocale(LC_CTYPE, $oldlocale); | |
} | |
private function textEncodeString(&$str) { | |
$newstr = ''; | |
$i = 0; | |
$len = strlen($str); | |
while($i < $len) { | |
$ch = ord(substr($str, $i, 1)); | |
if ($ch == 0x22 || $ch == 0x5C) { | |
// escape double quote, backslash | |
$newstr .= '\\' . chr($ch); | |
$i++; | |
} else if ($ch >= 0x07 && $ch <= 0x0D ){ | |
// control characters with escape sequences | |
$newstr .= '\\' . substr('abtnvfr', $ch - 7, 1); | |
$i++; | |
} else if ($ch < 32) { | |
// other non-printable characters escaped as unicode | |
$newstr .= sprintf('\U%04x', $ch); | |
$i++; | |
} else if ($ch < 128) { | |
// ascii printable | |
$newstr .= chr($ch); | |
$i++; | |
} else if ($ch == 192 || $ch == 193) { | |
// invalid encoding of ASCII characters | |
$i++; | |
} else if (($ch & 0xC0) == 0x80){ | |
// part of a lost multibyte sequence, skip | |
$i++; | |
} else if (($ch & 0xE0) == 0xC0) { | |
// U+0080 - U+07FF (2 bytes) | |
$u = (($ch & 0x1F) << 6) | (ord(substr($str, $i+1, 1)) & 0x3F); | |
$newstr .= sprintf('\U%04x', $u); | |
$i += 2; | |
} else if (($ch & 0xF0) == 0xE0) { | |
// U+0800 - U+FFFF (3 bytes) | |
$u = (($ch & 0x0F) << 12) | ((ord(substr($str, $i+1, 1)) & 0x3F) << 6) | (ord(substr($str, $i+2, 1)) & 0x3F); | |
$newstr .= sprintf('\U%04x', $u); | |
$i += 3; | |
} else if (($ch & 0xF8) == 0xF0) { | |
// U+10000 - U+3FFFF (4 bytes) | |
$u = (($ch & 0x07) << 18) | ((ord(substr($str, $i+1, 1)) & 0x3F) << 12) | ((ord(substr($str, $i+2, 1)) & 0x3F) << 6) | (ord(substr($str, $i+3, 1)) & 0x3F); | |
$newstr .= sprintf('\U%04x', $u); | |
$i += 4; | |
} else { | |
// 5 and 6 byte sequences are not valid UTF-8 | |
$i++; | |
} | |
} | |
return $newstr; | |
} | |
private function textWriteDict(&$text, &$dict, $indentLevel) { | |
if (count($dict) == 0) { | |
$text .= '{}'; | |
return; | |
} | |
$text .= "{\n"; | |
$indent = ''; | |
$indentLevel++; | |
while(strlen($indent) < $indentLevel) $indent .= "\t"; | |
foreach($dict as $k => &$v) { | |
$text .= $indent; | |
$this->textWriteValue($text, $k); | |
$text .= ' = '; | |
$this->textWriteValue($text, $v, $indentLevel); | |
$text .= ";\n"; | |
} | |
$text .= substr($indent, 0, -1) . '}'; | |
} | |
private function textWriteArray(&$text, &$arr, $indentLevel) { | |
if (count($arr) == 0) { | |
$text .= '()'; | |
return; | |
} | |
$text .= "(\n"; | |
$indent = ''; | |
$indentLevel++; | |
while(strlen($indent) < $indentLevel) $indent .= "\t"; | |
foreach($arr as &$v) { | |
$text .= $indent; | |
$this->textWriteValue($text, $v, $indentLevel); | |
$text .= ",\n"; | |
} | |
$text .= substr($indent, 0, -1) . ')'; | |
} | |
} | |
class PlistData | |
{ | |
private $data; | |
public function __construct($str) { | |
$this->data = $str; | |
} | |
public function base64EncodedData () { | |
return base64_encode($this->data); | |
} | |
public function hexEncodedData () { | |
$len = strlen($this->data); | |
$hexstr = ''; | |
for($i = 0; $i < $len; $i += 4) | |
$hexstr .= bin2hex(substr($this->data, $i, 4)) . ' '; | |
return substr($hexstr, 0, -1); | |
} | |
} | |
class PlistDate | |
{ | |
private $dateval; | |
public function __construct($init = NULL) { | |
if (is_int($init)) | |
$this->dateval = $init; | |
elseif (is_string($init)) | |
$this->dateval = strtotime($init); | |
elseif ($init == NULL) | |
$this->dateval = time(); | |
} | |
public function ISO8601Date() { | |
return gmdate('Y-m-d\TH:i:s\Z', $this->dateval); | |
} | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment