CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Reporting Security Issues
    • Privacy Policy
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Get Involved
    • Issues (GitHub)
    • Bakery
    • Featured Resources
    • Training
    • Meetups
    • My CakePHP
    • CakeFest
    • Newsletter
    • Linkedin
    • YouTube
    • Facebook
    • Twitter
    • Mastodon
    • Help & Support
    • Forum
    • Stack Overflow
    • Slack
    • Paid Support
CakePHP

C CakePHP 2.1 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.1
      • 4.2
      • 4.1
      • 4.0
      • 3.9
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Packages

  • Cake
    • Cache
      • Engine
    • Configure
    • Console
      • Command
        • Task
    • Controller
      • Component
        • Acl
        • Auth
    • Core
    • Error
    • Event
    • I18n
    • Log
      • Engine
    • Model
      • Behavior
      • Datasource
        • Database
        • Session
    • Network
      • Email
      • Http
    • Routing
      • Route
    • TestSuite
      • Coverage
      • Fixture
      • Reporter
    • Utility
    • View
      • Helper

Classes

  • CakeNumber
  • CakeTime
  • ClassRegistry
  • Debugger
  • File
  • Folder
  • Inflector
  • ObjectCollection
  • Sanitize
  • Security
  • Set
  • String
  • Validation
  • Xml
  1: <?php
  2: /**
  3:  * XML handling for Cake.
  4:  *
  5:  * The methods in these classes enable the datasources that use XML to work.
  6:  *
  7:  * PHP 5
  8:  *
  9:  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 10:  * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
 11:  *
 12:  * Licensed under The MIT License
 13:  * Redistributions of files must retain the above copyright notice.
 14:  *
 15:  * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
 16:  * @link          http://cakephp.org CakePHP(tm) Project
 17:  * @package       Cake.Utility
 18:  * @since         CakePHP v .0.10.3.1400
 19:  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
 20:  */
 21: 
 22: /**
 23:  * XML handling for Cake.
 24:  *
 25:  * The methods in these classes enable the datasources that use XML to work.
 26:  *
 27:  * @package       Cake.Utility
 28:  */
 29: class Xml {
 30: 
 31: /**
 32:  * Initialize SimpleXMLElement or DOMDocument from a given XML string, file path, URL or array.
 33:  *
 34:  * ### Usage:
 35:  *
 36:  * Building XML from a string:
 37:  *
 38:  * `$xml = Xml::build('<example>text</example>');`
 39:  *
 40:  * Building XML from string (output DOMDocument):
 41:  *
 42:  * `$xml = Xml::build('<example>text</example>', array('return' => 'domdocument'));`
 43:  *
 44:  * Building XML from a file path:
 45:  *
 46:  * `$xml = Xml::build('/path/to/an/xml/file.xml');`
 47:  *
 48:  * Building from a remote URL:
 49:  *
 50:  * `$xml = Xml::build('http://example.com/example.xml');`
 51:  *
 52:  * Building from an array:
 53:  *
 54:  * {{{
 55:  *  $value = array(
 56:  *      'tags' => array(
 57:  *          'tag' => array(
 58:  *              array(
 59:  *                  'id' => '1',
 60:  *                  'name' => 'defect'
 61:  *              ),
 62:  *              array(
 63:  *                  'id' => '2',
 64:  *                  'name' => 'enhancement'
 65:  *              )
 66:  *          )
 67:  *      )
 68:  *  );
 69:  * $xml = Xml::build($value);
 70:  * }}}
 71:  *
 72:  * When building XML from an array ensure that there is only one top level element.
 73:  *
 74:  * ### Options
 75:  *
 76:  * - `return` Can be 'simplexml' to return object of SimpleXMLElement or 'domdocument' to return DOMDocument.
 77:  * - `loadEntities` Defaults to false.  Set to true to enable loading of `<!ENTITY` definitions.  This
 78:  *   is disabled by default for security reasons.
 79:  * - If using array as input, you can pass `options` from Xml::fromArray.
 80:  *
 81:  * @param mixed $input XML string, a path to a file, an URL or an array
 82:  * @param array $options The options to use
 83:  * @return SimpleXMLElement|DOMDocument SimpleXMLElement or DOMDocument
 84:  * @throws XmlException
 85:  */
 86:     public static function build($input, $options = array()) {
 87:         if (!is_array($options)) {
 88:             $options = array('return' => (string)$options);
 89:         }
 90:         $defaults = array(
 91:             'return' => 'simplexml',
 92:             'loadEntities' => false,
 93:         );
 94:         $options = array_merge($defaults, $options);
 95: 
 96:         if (is_array($input) || is_object($input)) {
 97:             return self::fromArray((array)$input, $options);
 98:         } elseif (strpos($input, '<') !== false) {
 99:             return self::_loadXml($input, $options);
100:         } elseif (file_exists($input) || strpos($input, 'http://') === 0 || strpos($input, 'https://') === 0) {
101:             $input = file_get_contents($input);
102:             return self::_loadXml($input, $options);
103:         } elseif (!is_string($input)) {
104:             throw new XmlException(__d('cake_dev', 'Invalid input.'));
105:         }
106:         throw new XmlException(__d('cake_dev', 'XML cannot be read.'));
107:     }
108: 
109: /**
110:  * Parse the input data and create either a SimpleXmlElement object or a DOMDocument.
111:  *
112:  * @param string $input The input to load.
113:  * @param array  $options The options to use. See Xml::build()
114:  * @return SimpleXmlElement|DOMDocument.
115:  */
116:     protected static function _loadXml($input, $options) {
117:         $hasDisable = function_exists('libxml_disable_entity_loader');
118:         $internalErrors = libxml_use_internal_errors(true);
119:         if ($hasDisable && !$options['loadEntities']) {
120:             libxml_disable_entity_loader(true);
121:         }
122:         if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') {
123:             $xml = new SimpleXMLElement($input, LIBXML_NOCDATA);
124:         } else {
125:             $xml = new DOMDocument();
126:             $xml->loadXML($input);
127:         }
128:         if ($hasDisable && !$options['loadEntities']) {
129:             libxml_disable_entity_loader(false);
130:         }
131:         libxml_use_internal_errors($internalErrors);
132:         return $xml;
133:     }
134: 
135: /**
136:  * Transform an array into a SimpleXMLElement
137:  *
138:  * ### Options
139:  *
140:  * - `format` If create childs ('tags') or attributes ('attribute').
141:  * - `version` Version of XML document. Default is 1.0.
142:  * - `encoding` Encoding of XML document. If null remove from XML header. Default is the some of application.
143:  * - `return` If return object of SimpleXMLElement ('simplexml') or DOMDocument ('domdocument'). Default is SimpleXMLElement.
144:  *
145:  * Using the following data:
146:  *
147:  * {{{
148:  * $value = array(
149:  *    'root' => array(
150:  *        'tag' => array(
151:  *            'id' => 1,
152:  *            'value' => 'defect',
153:  *            '@' => 'description'
154:  *         )
155:  *     )
156:  * );
157:  * }}}
158:  *
159:  * Calling `Xml::fromArray($value, 'tags');`  Will generate:
160:  *
161:  * `<root><tag><id>1</id><value>defect</value>description</tag></root>`
162:  *
163:  * And calling `Xml::fromArray($value, 'attribute');` Will generate:
164:  *
165:  * `<root><tag id="1" value="defect">description</tag></root>`
166:  *
167:  * @param array $input Array with data
168:  * @param array $options The options to use
169:  * @return SimpleXMLElement|DOMDocument SimpleXMLElement or DOMDocument
170:  * @throws XmlException
171:  */
172:     public static function fromArray($input, $options = array()) {
173:         if (!is_array($input) || count($input) !== 1) {
174:             throw new XmlException(__d('cake_dev', 'Invalid input.'));
175:         }
176:         $key = key($input);
177:         if (is_integer($key)) {
178:             throw new XmlException(__d('cake_dev', 'The key of input must be alphanumeric'));
179:         }
180: 
181:         if (!is_array($options)) {
182:             $options = array('format' => (string)$options);
183:         }
184:         $defaults = array(
185:             'format' => 'tags',
186:             'version' => '1.0',
187:             'encoding' => Configure::read('App.encoding'),
188:             'return' => 'simplexml'
189:         );
190:         $options = array_merge($defaults, $options);
191: 
192:         $dom = new DOMDocument($options['version'], $options['encoding']);
193:         self::_fromArray($dom, $dom, $input, $options['format']);
194: 
195:         $options['return'] = strtolower($options['return']);
196:         if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') {
197:             return new SimpleXMLElement($dom->saveXML());
198:         }
199:         return $dom;
200:     }
201: 
202: /**
203:  * Recursive method to create childs from array
204:  *
205:  * @param DOMDocument $dom Handler to DOMDocument
206:  * @param DOMElement $node Handler to DOMElement (child)
207:  * @param array $data Array of data to append to the $node.
208:  * @param string $format Either 'attribute' or 'tags'.  This determines where nested keys go.
209:  * @return void
210:  * @throws XmlException
211:  */
212:     protected static function _fromArray($dom, $node, &$data, $format) {
213:         if (empty($data) || !is_array($data)) {
214:             return;
215:         }
216:         foreach ($data as $key => $value) {
217:             if (is_string($key)) {
218:                 if (!is_array($value)) {
219:                     if (is_bool($value)) {
220:                         $value = (int)$value;
221:                     } elseif ($value === null) {
222:                         $value = '';
223:                     }
224:                     $isNamespace = strpos($key, 'xmlns:');
225:                     if ($isNamespace !== false) {
226:                         $node->setAttributeNS('http://www.w3.org/2000/xmlns/', $key, $value);
227:                         continue;
228:                     }
229:                     if ($key[0] !== '@' && $format === 'tags') {
230:                         $child = null;
231:                         if (!is_numeric($value)) {
232:                             // Escape special characters
233:                             // http://www.w3.org/TR/REC-xml/#syntax
234:                             // https://bugs.php.net/bug.php?id=36795
235:                             $child = $dom->createElement($key, '');
236:                             $child->appendChild(new DOMText($value));
237:                         } else {
238:                             $child = $dom->createElement($key, $value);
239:                         }
240:                         $node->appendChild($child);
241:                     } else {
242:                         if ($key[0] === '@') {
243:                             $key = substr($key, 1);
244:                         }
245:                         $attribute = $dom->createAttribute($key);
246:                         $attribute->appendChild($dom->createTextNode($value));
247:                         $node->appendChild($attribute);
248:                     }
249:                 } else {
250:                     if ($key[0] === '@') {
251:                         throw new XmlException(__d('cake_dev', 'Invalid array'));
252:                     }
253:                     if (is_numeric(implode('', array_keys($value)))) { // List
254:                         foreach ($value as $item) {
255:                             $itemData = compact('dom', 'node', 'key', 'format');
256:                             $itemData['value'] = $item;
257:                             self::_createChild($itemData);
258:                         }
259:                     } else { // Struct
260:                         self::_createChild(compact('dom', 'node', 'key', 'value', 'format'));
261:                     }
262:                 }
263:             } else {
264:                 throw new XmlException(__d('cake_dev', 'Invalid array'));
265:             }
266:         }
267:     }
268: 
269: /**
270:  * Helper to _fromArray(). It will create childs of arrays
271:  *
272:  * @param array $data Array with informations to create childs
273:  * @return void
274:  */
275:     protected static function _createChild($data) {
276:         extract($data);
277:         $childNS = $childValue = null;
278:         if (is_array($value)) {
279:             if (isset($value['@'])) {
280:                 $childValue = (string)$value['@'];
281:                 unset($value['@']);
282:             }
283:             if (isset($value['xmlns:'])) {
284:                 $childNS = $value['xmlns:'];
285:                 unset($value['xmlns:']);
286:             }
287:         } elseif (!empty($value) || $value === 0) {
288:             $childValue = (string)$value;
289:         }
290: 
291:         if ($childValue) {
292:             $child = $dom->createElement($key, $childValue);
293:         } else {
294:             $child = $dom->createElement($key);
295:         }
296:         if ($childNS) {
297:             $child->setAttribute('xmlns', $childNS);
298:         }
299: 
300:         self::_fromArray($dom, $child, $value, $format);
301:         $node->appendChild($child);
302:     }
303: 
304: /**
305:  * Returns this XML structure as a array.
306:  *
307:  * @param SimpleXMLElement|DOMDocument|DOMNode $obj SimpleXMLElement, DOMDocument or DOMNode instance
308:  * @return array Array representation of the XML structure.
309:  * @throws XmlException
310:  */
311:     public static function toArray($obj) {
312:         if ($obj instanceof DOMNode) {
313:             $obj = simplexml_import_dom($obj);
314:         }
315:         if (!($obj instanceof SimpleXMLElement)) {
316:             throw new XmlException(__d('cake_dev', 'The input is not instance of SimpleXMLElement, DOMDocument or DOMNode.'));
317:         }
318:         $result = array();
319:         $namespaces = array_merge(array('' => ''), $obj->getNamespaces(true));
320:         self::_toArray($obj, $result, '', array_keys($namespaces));
321:         return $result;
322:     }
323: 
324: /**
325:  * Recursive method to toArray
326:  *
327:  * @param SimpleXMLElement $xml SimpleXMLElement object
328:  * @param array $parentData Parent array with data
329:  * @param string $ns Namespace of current child
330:  * @param array $namespaces List of namespaces in XML
331:  * @return void
332:  */
333:     protected static function _toArray($xml, &$parentData, $ns, $namespaces) {
334:         $data = array();
335: 
336:         foreach ($namespaces as $namespace) {
337:             foreach ($xml->attributes($namespace, true) as $key => $value) {
338:                 if (!empty($namespace)) {
339:                     $key = $namespace . ':' . $key;
340:                 }
341:                 $data['@' . $key] = (string)$value;
342:             }
343: 
344:             foreach ($xml->children($namespace, true) as $child) {
345:                 self::_toArray($child, $data, $namespace, $namespaces);
346:             }
347:         }
348: 
349:         $asString = trim((string)$xml);
350:         if (empty($data)) {
351:             $data = $asString;
352:         } elseif (!empty($asString)) {
353:             $data['@'] = $asString;
354:         }
355: 
356:         if (!empty($ns)) {
357:             $ns .= ':';
358:         }
359:         $name = $ns . $xml->getName();
360:         if (isset($parentData[$name])) {
361:             if (!is_array($parentData[$name]) || !isset($parentData[$name][0])) {
362:                 $parentData[$name] = array($parentData[$name]);
363:             }
364:             $parentData[$name][] = $data;
365:         } else {
366:             $parentData[$name] = $data;
367:         }
368:     }
369: 
370: }
371: 
OpenHub
Rackspace
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Reporting Security Issues
  • Privacy Policy
  • Logos & Trademarks
  • Community
  • Get Involved
  • Issues (GitHub)
  • Bakery
  • Featured Resources
  • Training
  • Meetups
  • My CakePHP
  • CakeFest
  • Newsletter
  • Linkedin
  • YouTube
  • Facebook
  • Twitter
  • Mastodon
  • Help & Support
  • Forum
  • Stack Overflow
  • Slack
  • Paid Support

Generated using CakePHP API Docs