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

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