Permalink
Browse files

Adding new plist parser class + examples.

  • Loading branch information...
1 parent ce15f21 commit 8298a9e05ec8f64c378e0d1901de6a98fb403b97 @jsjohnst committed Oct 27, 2008
View
1 README
@@ -7,3 +7,4 @@ Included functionality
Enum class - provides a PHP implementation of C style enums
EnumIterator class - provides an interator to iterate all the members of an Enum type
+PlistParser class - provides an XML parser to parse Apple OS X plist files
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>Mozy</string>
+ <key>CFBundleGetInfoString</key>
+ <string>Mozy 0.9.0.0, ©2006-2007 Mozy, Inc.</string>
+ <key>CFBundleIconFile</key>
+ <string>MozyHome</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.berkeleydata.Config</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>Mozy</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>0.9.0.0</string>
+ <key>CFBundleSignature</key>
+ <string>MOZY</string>
+ <key>CFBundleVersion</key>
+ <string>26912</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>10.4.0</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>©2006-2007 Mozy, Inc.</string>
+ <key>NSMainNibFile</key>
+ <string>ConfigMain</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
@@ -0,0 +1,129 @@
+<?php
+
+class plistParser extends XMLReader
+{
+ public function parse($file) {
+ if(basename($file) == $file) {
+ throw new Exception("Non-relative file path expected", 1);
+ }
+ $this->open("file://" . $file);
+
+ // plist's always start with a doctype, use it as a validity check
+ $this->read();
+ if($this->nodeType !== XMLReader::DOC_TYPE || $this->name !== "plist") {
+ throw new Exception(sprintf("Error parsing plist. nodeType: %d -- Name: %s", $this->nodeType, $this->name), 2);
+ }
+
+ // as one additional check, the first element node is always a plist
+ if(!$this->next("plist") || $this->nodeType !== XMLReader::ELEMENT || $this->name !== "plist") {
+ throw new Exception(sprintf("Error parsing plist. nodeType: %d -- Name: %s", $this->nodeType, $this->name), 3);
+ }
+
+ $plist = array();
+ while($this->read()) {
+ if($this->nodeType == XMLReader::ELEMENT) {
+ $plist[] = $this->parse_node();
+ }
+ }
+ if(count($plist) == 1 && $plist[0]) {
+ // Most plists have a dict as their outer most tag
+ // So instead of returning an array with only one element
+ // return the contents of the dict instead
+ return $plist[0];
+ } else {
+ return $plist;
+ }
+ }
+
+ private function parse_node() {
+ // If not an element, nothing for us to do
+ if($this->nodeType !== XMLReader::ELEMENT) return;
+
+ switch($this->name) {
+ case 'data':
+ return base64_decode($this->getNodeText());
+ break;
+ case 'real':
+ return floatval($this->getNodeText());
+ break;
+ case 'string':
+ return $this->getNodeText();
+ break;
+ case 'integer':
+ return intval($this->getNodeText());
+ break;
+ case 'date':
+ return $this->getNodeText();
+ break;
+ case 'true':
+ return true;
+ break;
+ case 'false':
+ return false;
+ break;
+ case 'array':
+ return $this->parse_array();
+ break;
+ case 'dict':
+ return $this->parse_dict();
+ break;
+ default:
+ // per DTD, the above is the only valid types
+ throw new Exception(sprintf("Not a valid plist. %s is not a valid type", $this->name), 4);
+ }
+ }
+
+ private function parse_dict() {
+ $array = array();
+ $this->nextOfType(XMLReader::ELEMENT);
+ do {
+ if($this->nodeType !== XMLReader::ELEMENT || $this->name !== "key") {
+ // If we aren't on a key, then jump to the next key
+ // per DTD, dicts have to have <key><somevalue> and nothing else
+ if(!$this->next("key")) {
+ // no more keys left so per DTD we are done with this dict
+ return $array;
+ }
+ }
+ $key = $this->getNodeText();
+ $this->nextOfType(XMLReader::ELEMENT);
+ $array[$key] = $this->parse_node();
+ $this->nextOfType(XMLReader::ELEMENT, XMLReader::END_ELEMENT);
+ } while(!$this->isNodeOfTypeName(XMLReader::END_ELEMENT, "dict"));
+ return $array;
+ }
+
+ private function parse_array() {
+ $array = array();
+ do {
+ // find the first item of the array and append it onto the node list
+ $this->nextOfType(XMLReader::ELEMENT);
+ $array[] = $this->parse_node();
+ // skip over any whitespace
+ $this->nextOfType(XMLReader::ELEMENT, XMLReader::END_ELEMENT);
+ } while(!$this->isNodeOfTypeName(XMLReader::END_ELEMENT, "array"));
+ return $array;
+ }
+
+ private function getNodeText() {
+ $string = $this->readString();
+ // now gobble up everything up to the closing tag
+ $this->nextOfType(XMLReader::END_ELEMENT);
+ return $string;
+ }
+
+ private function nextOfType() {
+ $types = func_get_args();
+ // skip to next
+ $this->read();
+ // check if it's one of the types requested and loop until it's one we want
+ while(!(in_array($this->nodeType, $types))) {
+ // node isn't of type requested, so keep going
+ $this->read();
+ }
+ }
+
+ private function isNodeOfTypeName($type, $name) {
+ return $this->nodeType === $type && $this->name === $name;
+ }
+}
@@ -0,0 +1,7 @@
+<?php
+
+include("PlistParser.inc");
+
+$parser = new plistParser();
+$plist = $parser->parse(dirname(__FILE__) . "/iTunes.xml");
+var_dump($plist);
@@ -0,0 +1,7 @@
+<?php
+
+include("PlistParser.inc");
+
+$parser = new plistParser();
+$plist = $parser->parse(dirname(__FILE__) . "/Info.plist");
+var_dump($plist);
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Major Version</key><integer>1</integer>
+ <key>Minor Version</key><integer>1</integer>
+ <key>Application Version</key><string>8.0.1</string>
+ <key>Features</key><integer>5</integer>
+ <key>Show Content Ratings</key><true/>
+ <key>Music Folder</key><string>file://localhost/Users/testUser/Music/iTunes/iTunes%20Music/</string>
+ <key>Library Persistent ID</key><string>C39203948AF3D3E</string>
+ <key>Tracks</key>
+ <dict>
+ <key>1</key>
+ <dict>
+ <key>Track ID</key><integer>1</integer>
+ <key>Name</key><string>My Track</string>
+ <key>Artist</key><string>My Artist</string>
+ <key>Album</key><string>My Album</string>
+ <key>Genre</key><string>My Genre</string>
+ <key>Kind</key><string>MPEG audio file</string>
+ <key>Size</key><integer>123456</integer>
+ <key>Total Time</key><integer>123456</integer>
+ <key>Track Number</key><integer>1</integer>
+ <key>Year</key><integer>2008</integer>
+ <key>Date Modified</key><date>2008-03-03T03:33:33Z</date>
+ <key>Date Added</key><date>2008-03-03T03:33:33Z</date>
+ <key>Bit Rate</key><integer>128</integer>
+ <key>Sample Rate</key><integer>44100</integer>
+ <key>Comments</key><string>All Rights Reserved.</string>
+ <key>Release Date</key><date>2007-03-12T04:01:37Z</date>
+ <key>Persistent ID</key><string>C3E339393939E3</string>
+ <key>Track Type</key><string>File</string>
+ <key>Podcast</key><false/>
+ <key>Unplayed</key><true/>
+ <key>File Type</key><integer>123456</integer>
+ <key>File Creator</key><integer>123456</integer>
+ <key>Location</key><string>file://localhost/Users/testUser/Music/iTunes/iTunes%20Music/my.mp3</string>
+ <key>File Folder Count</key><integer>4</integer>
+ <key>Library Folder Count</key><integer>1</integer>
+ </dict>
+ </dict>
+</dict>
+</plist>

0 comments on commit 8298a9e

Please sign in to comment.