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

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