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.2 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.2
      • 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
      • Validator
    • Network
      • Email
      • Http
    • Routing
      • Filter
      • Route
    • TestSuite
      • Coverage
      • Fixture
      • Reporter
    • Utility
    • View
      • Helper

Classes

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