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

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.9
      • 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
  • None

Classes

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