Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 39 and 311]

   1  <?php
   2  /**
   3   * LICENSE
   4   *
   5   * This file is part of CFPropertyList.
   6   *
   7   * The PHP implementation of Apple's PropertyList can handle XML PropertyLists
   8   * as well as binary PropertyLists. It offers functionality to easily convert
   9   * data between worlds, e.g. recalculating timestamps from unix epoch to apple
  10   * epoch and vice versa. A feature to automagically create (guess) the plist
  11   * structure from a normal PHP data structure will help you dump your data to
  12   * plist in no time.
  13   *
  14   * Copyright (c) 2018 Teclib'
  15   *
  16   * Permission is hereby granted, free of charge, to any person obtaining a copy
  17   * of this software and associated documentation files (the "Software"), to deal
  18   * in the Software without restriction, including without limitation the rights
  19   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  20   * copies of the Software, and to permit persons to whom the Software is
  21   * furnished to do so, subject to the following conditions:
  22   *
  23   * The above copyright notice and this permission notice shall be included in all
  24   * copies or substantial portions of the Software.
  25   *
  26   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  27   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  28   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  29   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  30   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  31   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  32   * SOFTWARE.
  33   *
  34   * ------------------------------------------------------------------------------
  35   * @author    Rodney Rehm <rodney.rehm@medialize.de>
  36   * @author    Christian Kruse <cjk@wwwtech.de>
  37   * @copyright Copyright © 2018 Teclib
  38   * @package   plist
  39   * @license   MIT
  40   * @link      https://github.com/TECLIB/CFPropertyList/
  41   * @link      http://developer.apple.com/documentation/Darwin/Reference/ManPages/man5/plist.5.html Property Lists
  42   * ------------------------------------------------------------------------------
  43   */
  44  
  45  namespace CFPropertyList;
  46  
  47  use DateTime;
  48  use Iterator;
  49  use stdClass;
  50  
  51   /**
  52    * CFTypeDetector
  53    * Interface for converting native PHP data structures to CFPropertyList objects.
  54    * @author Rodney Rehm <rodney.rehm@medialize.de>
  55    * @author Christian Kruse <cjk@wwwtech.de>
  56    * @package plist
  57    * @subpackage plist.types
  58    * @example example-create-02.php Using CFTypeDetector
  59    * @example example-create-03.php Using CFTypeDetector with CFDate and CFData
  60    * @example example-create-04.php Using and extended CFTypeDetector
  61    */
  62  class CFTypeDetector
  63  {
  64  
  65    /**
  66     * flag stating if all arrays should automatically be converted to CFDictionary
  67     * @var boolean
  68     */
  69      protected $autoDictionary = false;
  70  
  71    /**
  72     * flag stating if exceptions should be suppressed or thrown
  73     * @var boolean
  74     */
  75      protected $suppressExceptions = false;
  76  
  77    /**
  78     * name of a method that will be used for array to object conversations
  79     * @var callable
  80     */
  81      protected $objectToArrayMethod = null;
  82  
  83    /**
  84     * flag stating if "123.23" should be converted to float (true) or preserved as string (false)
  85     * @var boolean
  86     */
  87      protected $castNumericStrings = true;
  88  
  89  
  90    /**
  91     * Create new CFTypeDetector
  92     * @param array $options Configuration for casting values [autoDictionary, suppressExceptions, objectToArrayMethod, castNumericStrings]
  93     */
  94      public function __construct(array $options = array())
  95      {
  96        //$autoDicitionary=false,$suppressExceptions=false,$objectToArrayMethod=null
  97          foreach ($options as $key => $value) {
  98              if (property_exists($this, $key)) {
  99                  $this->$key = $value;
 100              }
 101          }
 102      }
 103  
 104    /**
 105     * Determine if an array is associative or numerical.
 106     * Numerical Arrays have incrementing index-numbers that don't contain gaps.
 107     * @param array $value Array to check indexes of
 108     * @return boolean true if array is associative, false if array has numeric indexes
 109     */
 110      protected function isAssociativeArray($value)
 111      {
 112          $numericKeys = true;
 113          $i = 0;
 114          foreach ($value as $key => $v) {
 115              if ($i !== $key) {
 116                  $numericKeys = false;
 117                  break;
 118              }
 119              $i++;
 120          }
 121          return !$numericKeys;
 122      }
 123  
 124    /**
 125     * Get the default value
 126     * @return CFType the default value to return if no suitable type could be determined
 127     */
 128      protected function defaultValue()
 129      {
 130          return new CFString();
 131      }
 132  
 133    /**
 134     * Create CFType-structure by guessing the data-types.
 135     * CFArray, {@link CFDictionary}, {@link CFBoolean}, {@link CFNumber} and {@link CFString} can be created, {@link CFDate} and {@link CFData} cannot.
 136     * <br /><b>Note:</b>Distinguishing between {@link CFArray} and {@link CFDictionary} is done by examining the keys.
 137     * Keys must be strictly incrementing integers to evaluate to a {@link CFArray}.
 138     * Since PHP does not offer a function to test for associative arrays,
 139     * this test causes the input array to be walked twice and thus work rather slow on large collections.
 140     * If you work with large arrays and can live with all arrays evaluating to {@link CFDictionary},
 141     * feel free to set the appropriate flag.
 142     * <br /><b>Note:</b> If $value is an instance of CFType it is simply returned.
 143     * <br /><b>Note:</b> If $value is neither a CFType, array, numeric, boolean nor string, it is omitted.
 144     * @param mixed $value Value to convert to CFType
 145     * @return CFType CFType based on guessed type
 146     * @uses isAssociativeArray() to check if an array only has numeric indexes
 147     */
 148      public function toCFType($value)
 149      {
 150          switch (true) {
 151              case $value instanceof CFType:
 152                  return $value;
 153              break;
 154  
 155              case is_object($value):
 156                  // DateTime should be CFDate
 157                  if ($value instanceof DateTime) {
 158                      return new CFDate($value->getTimestamp());
 159                  }
 160  
 161                  // convert possible objects to arrays, arrays will be arrays
 162                  if ($this->objectToArrayMethod && is_callable(array($value, $this->objectToArrayMethod))) {
 163                      $value = call_user_func(array( $value, $this->objectToArrayMethod ));
 164                  } else if ($value instanceof stdClass) {
 165                      $value = (array) $value;
 166                  }
 167  
 168                  if (!is_array($value)) {
 169                      if ($this->suppressExceptions) {
 170                          return $this->defaultValue();
 171                      }
 172  
 173                      throw new PListException('Could not determine CFType for object of type '. get_class($value));
 174                  }
 175              /* break; omitted */
 176  
 177              case $value instanceof Iterator:
 178              case is_array($value):
 179                  // test if $value is simple or associative array
 180                  if (!$this->autoDictionary) {
 181                      if (!$this->isAssociativeArray($value)) {
 182                          $t = new CFArray();
 183                          foreach ($value as $v) {
 184                              $t->add($this->toCFType($v));
 185                          }
 186                          return $t;
 187                      }
 188                  }
 189  
 190                  $t = new CFDictionary();
 191                  foreach ($value as $k => $v) {
 192                      $t->add($k, $this->toCFType($v));
 193                  }
 194  
 195                  return $t;
 196              break;
 197  
 198              case is_bool($value):
 199                  return new CFBoolean($value);
 200              break;
 201  
 202              case is_null($value):
 203                  return new CFString();
 204              break;
 205  
 206              case is_resource($value):
 207                  if ($this->suppressExceptions) {
 208                      return $this->defaultValue();
 209                  }
 210  
 211                  throw new PListException('Could not determine CFType for resource of type '. get_resource_type($value));
 212              break;
 213  
 214              case is_numeric($value):
 215                  if (!$this->castNumericStrings && is_string($value)) {
 216                      return new CFString($value);
 217                  }
 218  
 219                  return new CFNumber($value);
 220              break;
 221  
 222              case is_string($value):
 223                  if (strpos($value, "\x00") !== false) {
 224                      return new CFData($value);
 225                  }
 226                  return new CFString($value);
 227  
 228              break;
 229  
 230              default:
 231                  if ($this->suppressExceptions) {
 232                      return $this->defaultValue();
 233                  }
 234  
 235                  throw new PListException('Could not determine CFType for '. gettype($value));
 236              break;
 237          }
 238      }
 239  }