Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]

   1  <?php
   2  /*
   3   * Copyright 2011 Google Inc.
   4   *
   5   * Licensed under the Apache License, Version 2.0 (the "License");
   6   * you may not use this file except in compliance with the License.
   7   * You may obtain a copy of the License at
   8   *
   9   *     http://www.apache.org/licenses/LICENSE-2.0
  10   *
  11   * Unless required by applicable law or agreed to in writing, software
  12   * distributed under the License is distributed on an "AS IS" BASIS,
  13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14   * See the License for the specific language governing permissions and
  15   * limitations under the License.
  16   */
  17  
  18  /**
  19   * This class defines attributes, valid values, and usage which is generated
  20   * from a given json schema.
  21   * http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5
  22   *
  23   */
  24  #[AllowDynamicProperties]
  25  class Google_Model implements ArrayAccess
  26  {
  27    /**
  28     * If you need to specify a NULL JSON value, use Google_Model::NULL_VALUE
  29     * instead - it will be replaced when converting to JSON with a real null.
  30     */
  31    const NULL_VALUE = "{}gapi-php-null";
  32    protected $internal_gapi_mappings = array();
  33    protected $modelData = array();
  34    protected $processed = array();
  35  
  36    /**
  37     * Polymorphic - accepts a variable number of arguments dependent
  38     * on the type of the model subclass.
  39     */
  40    final public function __construct()
  41    {
  42      if (func_num_args() == 1 && is_array(func_get_arg(0))) {
  43        // Initialize the model with the array's contents.
  44        $array = func_get_arg(0);
  45        $this->mapTypes($array);
  46      }
  47      $this->gapiInit();
  48    }
  49  
  50    /**
  51     * Getter that handles passthrough access to the data array, and lazy object creation.
  52     * @param string $key Property name.
  53     * @return mixed The value if any, or null.
  54     */
  55    public function __get($key)
  56    {
  57      $keyTypeName = $this->keyType($key);
  58      $keyDataType = $this->dataType($key);
  59      if (isset($this->$keyTypeName) && !isset($this->processed[$key])) {
  60        if (isset($this->modelData[$key])) {
  61          $val = $this->modelData[$key];
  62        } else if (isset($this->$keyDataType) &&
  63            ($this->$keyDataType == 'array' || $this->$keyDataType == 'map')) {
  64          $val = array();
  65        } else {
  66          $val = null;
  67        }
  68  
  69        if ($this->isAssociativeArray($val)) {
  70          if (isset($this->$keyDataType) && 'map' == $this->$keyDataType) {
  71            foreach ($val as $arrayKey => $arrayItem) {
  72                $this->modelData[$key][$arrayKey] =
  73                  $this->createObjectFromName($keyTypeName, $arrayItem);
  74            }
  75          } else {
  76            $this->modelData[$key] = $this->createObjectFromName($keyTypeName, $val);
  77          }
  78        } else if (is_array($val)) {
  79          $arrayObject = array();
  80          foreach ($val as $arrayIndex => $arrayItem) {
  81            $arrayObject[$arrayIndex] =
  82              $this->createObjectFromName($keyTypeName, $arrayItem);
  83          }
  84          $this->modelData[$key] = $arrayObject;
  85        }
  86        $this->processed[$key] = true;
  87      }
  88  
  89      return isset($this->modelData[$key]) ? $this->modelData[$key] : null;
  90    }
  91  
  92    /**
  93     * Initialize this object's properties from an array.
  94     *
  95     * @param array $array Used to seed this object's properties.
  96     * @return void
  97     */
  98    protected function mapTypes($array)
  99    {
 100      // Hard initialise simple types, lazy load more complex ones.
 101      foreach ($array as $key => $val) {
 102        if ( !property_exists($this, $this->keyType($key)) &&
 103          property_exists($this, $key)) {
 104            $this->$key = $val;
 105            unset($array[$key]);
 106        } elseif (property_exists($this, $camelKey = Google_Utils::camelCase($key))) {
 107            // This checks if property exists as camelCase, leaving it in array as snake_case
 108            // in case of backwards compatibility issues.
 109            $this->$camelKey = $val;
 110        }
 111      }
 112      $this->modelData = $array;
 113    }
 114  
 115    /**
 116     * Blank initialiser to be used in subclasses to do  post-construction initialisation - this
 117     * avoids the need for subclasses to have to implement the variadics handling in their
 118     * constructors.
 119     */
 120    protected function gapiInit()
 121    {
 122      return;
 123    }
 124  
 125    /**
 126     * Create a simplified object suitable for straightforward
 127     * conversion to JSON. This is relatively expensive
 128     * due to the usage of reflection, but shouldn't be called
 129     * a whole lot, and is the most straightforward way to filter.
 130     */
 131    public function toSimpleObject()
 132    {
 133      $object = new stdClass();
 134  
 135      // Process all other data.
 136      foreach ($this->modelData as $key => $val) {
 137        $result = $this->getSimpleValue($val);
 138        if ($result !== null) {
 139          $object->$key = $this->nullPlaceholderCheck($result);
 140        }
 141      }
 142  
 143      // Process all public properties.
 144      $reflect = new ReflectionObject($this);
 145      $props = $reflect->getProperties(ReflectionProperty::IS_PUBLIC);
 146      foreach ($props as $member) {
 147        $name = $member->getName();
 148        $result = $this->getSimpleValue($this->$name);
 149        if ($result !== null) {
 150          $name = $this->getMappedName($name);
 151          $object->$name = $this->nullPlaceholderCheck($result);
 152        }
 153      }
 154  
 155      return $object;
 156    }
 157  
 158    /**
 159     * Handle different types of values, primarily
 160     * other objects and map and array data types.
 161     */
 162    private function getSimpleValue($value)
 163    {
 164      if ($value instanceof Google_Model) {
 165        return $value->toSimpleObject();
 166      } else if (is_array($value)) {
 167        $return = array();
 168        foreach ($value as $key => $a_value) {
 169          $a_value = $this->getSimpleValue($a_value);
 170          if ($a_value !== null) {
 171            $key = $this->getMappedName($key);
 172            $return[$key] = $this->nullPlaceholderCheck($a_value);
 173          }
 174        }
 175        return $return;
 176      }
 177      return $value;
 178    }
 179    
 180    /**
 181     * Check whether the value is the null placeholder and return true null.
 182     */
 183    private function nullPlaceholderCheck($value)
 184    {
 185      if ($value === self::NULL_VALUE) {
 186        return null;
 187      }
 188      return $value;
 189    }
 190  
 191    /**
 192     * If there is an internal name mapping, use that.
 193     */
 194    private function getMappedName($key)
 195    {
 196      if (isset($this->internal_gapi_mappings) &&
 197          isset($this->internal_gapi_mappings[$key])) {
 198        $key = $this->internal_gapi_mappings[$key];
 199      }
 200      return $key;
 201    }
 202  
 203    /**
 204     * Returns true only if the array is associative.
 205     * @param array $array
 206     * @return bool True if the array is associative.
 207     */
 208    protected function isAssociativeArray($array)
 209    {
 210      if (!is_array($array)) {
 211        return false;
 212      }
 213      $keys = array_keys($array);
 214      foreach ($keys as $key) {
 215        if (is_string($key)) {
 216          return true;
 217        }
 218      }
 219      return false;
 220    }
 221  
 222    /**
 223     * Given a variable name, discover its type.
 224     *
 225     * @param $name
 226     * @param $item
 227     * @return object The object from the item.
 228     */
 229    private function createObjectFromName($name, $item)
 230    {
 231      $type = $this->$name;
 232      return new $type($item);
 233    }
 234  
 235    /**
 236     * Verify if $obj is an array.
 237     * @throws Google_Exception Thrown if $obj isn't an array.
 238     * @param array $obj Items that should be validated.
 239     * @param string $method Method expecting an array as an argument.
 240     */
 241    public function assertIsArray($obj, $method)
 242    {
 243      if ($obj && !is_array($obj)) {
 244        throw new Google_Exception(
 245            "Incorrect parameter type passed to $method(). Expected an array."
 246        );
 247      }
 248    }
 249  
 250    public function offsetExists($offset): bool
 251    {
 252      return isset($this->$offset) || isset($this->modelData[$offset]);
 253    }
 254  
 255    #[\ReturnTypeWillChange]
 256    public function offsetGet($offset)
 257    {
 258      return isset($this->$offset) ?
 259          $this->$offset :
 260          $this->__get($offset);
 261    }
 262  
 263    public function offsetSet($offset, $value): void
 264    {
 265      if (property_exists($this, $offset)) {
 266        $this->$offset = $value;
 267      } else {
 268        $this->modelData[$offset] = $value;
 269        $this->processed[$offset] = true;
 270      }
 271    }
 272  
 273    public function offsetUnset($offset): void
 274    {
 275      unset($this->modelData[$offset]);
 276    }
 277  
 278    protected function keyType($key)
 279    {
 280      return $key . "Type";
 281    }
 282  
 283    protected function dataType($key)
 284    {
 285      return $key . "DataType";
 286    }
 287  
 288    public function __isset($key)
 289    {
 290      return isset($this->modelData[$key]);
 291    }
 292  
 293    public function __unset($key)
 294    {
 295      unset($this->modelData[$key]);
 296    }
 297  }