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

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