1<?php 2 3/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 4 5/** 6 * This file is part of the PEAR Console_CommandLine package. 7 * 8 * PHP version 5 9 * 10 * LICENSE: This source file is subject to the MIT license that is available 11 * through the world-wide-web at the following URI: 12 * http://opensource.org/licenses/mit-license.php 13 * 14 * @category Console 15 * @package Console_CommandLine 16 * @author David JEAN LOUIS <izimobil@gmail.com> 17 * @copyright 2007 David JEAN LOUIS 18 * @license http://opensource.org/licenses/mit-license.php MIT License 19 * @version CVS: $Id$ 20 * @link http://pear.php.net/package/Console_CommandLine 21 * @since File available since release 0.1.0 22 * @filesource 23 */ 24 25/** 26 * Required file 27 */ 28require_once 'Console/CommandLine.php'; 29 30/** 31 * Parser for command line xml definitions. 32 * 33 * @category Console 34 * @package Console_CommandLine 35 * @author David JEAN LOUIS <izimobil@gmail.com> 36 * @copyright 2007 David JEAN LOUIS 37 * @license http://opensource.org/licenses/mit-license.php MIT License 38 * @version Release: @package_version@ 39 * @link http://pear.php.net/package/Console_CommandLine 40 * @since Class available since release 0.1.0 41 */ 42class Console_CommandLine_XmlParser 43{ 44 // parse() {{{ 45 46 /** 47 * Parses the given xml definition file and returns a 48 * Console_CommandLine instance constructed with the xml data. 49 * 50 * @param string $xmlfile The xml file to parse 51 * 52 * @return Console_CommandLine A parser instance 53 */ 54 public static function parse($xmlfile) 55 { 56 if (!is_readable($xmlfile)) { 57 Console_CommandLine::triggerError('invalid_xml_file', 58 E_USER_ERROR, array('{$file}' => $xmlfile)); 59 } 60 $doc = new DomDocument(); 61 $doc->load($xmlfile); 62 self::validate($doc); 63 $nodes = $doc->getElementsByTagName('command'); 64 $root = $nodes->item(0); 65 return self::_parseCommandNode($root, true); 66 } 67 68 // }}} 69 // parseString() {{{ 70 71 /** 72 * Parses the given xml definition string and returns a 73 * Console_CommandLine instance constructed with the xml data. 74 * 75 * @param string $xmlstr The xml string to parse 76 * 77 * @return Console_CommandLine A parser instance 78 */ 79 public static function parseString($xmlstr) 80 { 81 $doc = new DomDocument(); 82 $doc->loadXml($xmlstr); 83 self::validate($doc); 84 $nodes = $doc->getElementsByTagName('command'); 85 $root = $nodes->item(0); 86 return self::_parseCommandNode($root, true); 87 } 88 89 // }}} 90 // validate() {{{ 91 92 /** 93 * Validates the xml definition using Relax NG. 94 * 95 * @param DomDocument $doc The document to validate 96 * 97 * @return boolean Whether the xml data is valid or not. 98 * @throws Console_CommandLine_Exception 99 * @todo use exceptions 100 */ 101 public static function validate($doc) 102 { 103 $pkgRoot = __DIR__ . '/../../'; 104 $paths = array( 105 // PEAR/Composer 106 '@data_dir@/Console_CommandLine/data/xmlschema.rng', 107 // Composer 108 $pkgRoot . 'data/Console_CommandLine/data/xmlschema.rng', 109 $pkgRoot . 'data/console_commandline/data/xmlschema.rng', 110 // Git 111 $pkgRoot . 'data/xmlschema.rng', 112 'xmlschema.rng', 113 ); 114 115 foreach ($paths as $path) { 116 if (is_readable($path)) { 117 return $doc->relaxNGValidate($path); 118 } 119 } 120 Console_CommandLine::triggerError( 121 'invalid_xml_file', 122 E_USER_ERROR, array('{$file}' => $rngfile)); 123 } 124 125 // }}} 126 // _parseCommandNode() {{{ 127 128 /** 129 * Parses the root command node or a command node and returns the 130 * constructed Console_CommandLine or Console_CommandLine_Command instance. 131 * 132 * @param DomDocumentNode $node The node to parse 133 * @param bool $isRootNode Whether it is a root node or not 134 * 135 * @return mixed Console_CommandLine or Console_CommandLine_Command 136 */ 137 private static function _parseCommandNode($node, $isRootNode = false) 138 { 139 if ($isRootNode) { 140 $obj = new Console_CommandLine(); 141 } else { 142 include_once 'Console/CommandLine/Command.php'; 143 $obj = new Console_CommandLine_Command(); 144 } 145 foreach ($node->childNodes as $cNode) { 146 $cNodeName = $cNode->nodeName; 147 switch ($cNodeName) { 148 case 'name': 149 case 'description': 150 case 'version': 151 $obj->$cNodeName = trim($cNode->nodeValue); 152 break; 153 case 'add_help_option': 154 case 'add_version_option': 155 case 'force_posix': 156 $obj->$cNodeName = self::_bool(trim($cNode->nodeValue)); 157 break; 158 case 'option': 159 $obj->addOption(self::_parseOptionNode($cNode)); 160 break; 161 case 'argument': 162 $obj->addArgument(self::_parseArgumentNode($cNode)); 163 break; 164 case 'command': 165 $obj->addCommand(self::_parseCommandNode($cNode)); 166 break; 167 case 'aliases': 168 if (!$isRootNode) { 169 foreach ($cNode->childNodes as $subChildNode) { 170 if ($subChildNode->nodeName == 'alias') { 171 $obj->aliases[] = trim($subChildNode->nodeValue); 172 } 173 } 174 } 175 break; 176 case 'messages': 177 $obj->messages = self::_messages($cNode); 178 break; 179 default: 180 break; 181 } 182 } 183 return $obj; 184 } 185 186 // }}} 187 // _parseOptionNode() {{{ 188 189 /** 190 * Parses an option node and returns the constructed 191 * Console_CommandLine_Option instance. 192 * 193 * @param DomDocumentNode $node The node to parse 194 * 195 * @return Console_CommandLine_Option The built option 196 */ 197 private static function _parseOptionNode($node) 198 { 199 include_once 'Console/CommandLine/Option.php'; 200 $obj = new Console_CommandLine_Option($node->getAttribute('name')); 201 foreach ($node->childNodes as $cNode) { 202 $cNodeName = $cNode->nodeName; 203 switch ($cNodeName) { 204 case 'choices': 205 foreach ($cNode->childNodes as $subChildNode) { 206 if ($subChildNode->nodeName == 'choice') { 207 $obj->choices[] = trim($subChildNode->nodeValue); 208 } 209 } 210 break; 211 case 'messages': 212 $obj->messages = self::_messages($cNode); 213 break; 214 default: 215 if (property_exists($obj, $cNodeName)) { 216 $obj->$cNodeName = trim($cNode->nodeValue); 217 } 218 break; 219 } 220 } 221 if ($obj->action == 'Password') { 222 $obj->argument_optional = true; 223 } 224 return $obj; 225 } 226 227 // }}} 228 // _parseArgumentNode() {{{ 229 230 /** 231 * Parses an argument node and returns the constructed 232 * Console_CommandLine_Argument instance. 233 * 234 * @param DomDocumentNode $node The node to parse 235 * 236 * @return Console_CommandLine_Argument The built argument 237 */ 238 private static function _parseArgumentNode($node) 239 { 240 include_once 'Console/CommandLine/Argument.php'; 241 $obj = new Console_CommandLine_Argument($node->getAttribute('name')); 242 foreach ($node->childNodes as $cNode) { 243 $cNodeName = $cNode->nodeName; 244 switch ($cNodeName) { 245 case 'description': 246 case 'help_name': 247 case 'default': 248 $obj->$cNodeName = trim($cNode->nodeValue); 249 break; 250 case 'multiple': 251 $obj->multiple = self::_bool(trim($cNode->nodeValue)); 252 break; 253 case 'optional': 254 $obj->optional = self::_bool(trim($cNode->nodeValue)); 255 break; 256 case 'choices': 257 foreach ($cNode->childNodes as $subChildNode) { 258 if ($subChildNode->nodeName == 'choice') { 259 $obj->choices[] = trim($subChildNode->nodeValue); 260 } 261 } 262 break; 263 case 'messages': 264 $obj->messages = self::_messages($cNode); 265 break; 266 default: 267 break; 268 } 269 } 270 return $obj; 271 } 272 273 // }}} 274 // _bool() {{{ 275 276 /** 277 * Returns a boolean according to true/false possible strings. 278 * 279 * @param string $str The string to process 280 * 281 * @return boolean 282 */ 283 private static function _bool($str) 284 { 285 return in_array(strtolower((string)$str), array('true', '1', 'on', 'yes')); 286 } 287 288 // }}} 289 // _messages() {{{ 290 291 /** 292 * Returns an array of custom messages for the element 293 * 294 * @param DOMNode $node The messages node to process 295 * 296 * @return array an array of messages 297 * 298 * @see Console_CommandLine::$messages 299 * @see Console_CommandLine_Element::$messages 300 */ 301 private static function _messages(DOMNode $node) 302 { 303 $messages = array(); 304 305 foreach ($node->childNodes as $cNode) { 306 if ($cNode->nodeType == XML_ELEMENT_NODE) { 307 $name = $cNode->getAttribute('name'); 308 $value = trim($cNode->nodeValue); 309 310 $messages[$name] = $value; 311 } 312 } 313 314 return $messages; 315 } 316 317 // }}} 318} 319