validateModelType($modelType, true); // set initial data switch ($modelType) { case IMocker::DATA_TYPE_OBJECT: case IMocker::DATA_TYPE_ARRAY: $this->dataContainer = []; break; case IMocker::DATA_TYPE_INTEGER: case IMocker::DATA_TYPE_NUMBER: case IMocker::DATA_TYPE_STRING: case IMocker::DATA_TYPE_BOOLEAN: default: // scalar type $this->dataContainer = null; } } /** * Gets OAS 3.0 schema mapped to current class. * * @return array */ public static function getOpenApiSchema(): array { return json_decode(static::MODEL_SCHEMA, true); } /** * Creates new instance from provided data. * * @param mixed $data Data with values for new instance. * * @return OpenApiModelInterface */ public static function createFromData($data): OpenApiModelInterface { $instance = new static(); $instance->setData($data); return $instance; } /** * Sets instance data. * * @param mixed $data Data with values for new instance. * * @throws \InvalidArgumentException When value for array type is invalid. * * @return void */ public function setData($data): void { $schema = (array) static::getOpenApiSchema(); $modelType = (array_key_exists('type', $schema)) ? $schema['type'] : null; switch ($modelType) { case IMocker::DATA_TYPE_ARRAY: // data for array OAS type should be straight indexed array if (is_array($data)) { $arr = []; for ($i = 0; $i < count($data); $i++) { if (isset($data[$i])) { $arr[$i] = $data[$i]; } } if (count($arr) === count($data)) { $this->dataContainer = $arr; return; } } throw new InvalidArgumentException( sprintf('Invalid data for %s model because it accepts straight indexed arrays only', static::class) ); break; case IMocker::DATA_TYPE_OBJECT: foreach ($data as $key => $value) { // this action handles __set method $this->{$key} = $value; } break; case IMocker::DATA_TYPE_INTEGER: case IMocker::DATA_TYPE_NUMBER: case IMocker::DATA_TYPE_STRING: case IMocker::DATA_TYPE_BOOLEAN: default: $this->dataContainer = $data; break; } } /** * Returns instance data. * * @return mixed */ public function getData() { $data = null; $schema = (array) static::getOpenApiSchema(); $modelType = (array_key_exists('type', $schema)) ? $schema['type'] : null; switch ($modelType) { case IMocker::DATA_TYPE_OBJECT: // need to convert data container to object $data = new StdClass(); $definedProps = (array_key_exists('properties', $schema)) ? $schema['properties'] : null; if (is_array($definedProps) || is_object($definedProps)) { foreach ($definedProps as $propName => $propSchema) { if (array_key_exists($propName, $this->dataContainer)) { $data->{$propName} = $this->dataContainer[$propName]; } elseif (array_key_exists('required', $schema) && in_array($propName, $schema['required'])) { // property is required but not set $data->{$propName} = null; } } } break; case IMocker::DATA_TYPE_INTEGER: case IMocker::DATA_TYPE_NUMBER: case IMocker::DATA_TYPE_STRING: case IMocker::DATA_TYPE_BOOLEAN: case IMocker::DATA_TYPE_ARRAY: default: $data = $this->dataContainer; } return $data; } /** * Writes data to inaccessible (protected or private) or non-existing properties. * Ref @link https://www.php.net/manual/en/language.oop5.overloading.php#object.set * * @param string $param Property name. * @param mixed $value Property value. * * @throws \InvalidArgumentException When property doesn't exist in related OAS schema. * * @return void */ public function __set(string $param, $value): void { $schema = (array) static::getOpenApiSchema(); $modelType = (array_key_exists('type', $schema)) ? $schema['type'] : null; switch ($modelType) { case IMocker::DATA_TYPE_OBJECT: $definedProps = (array_key_exists('properties', $schema)) ? (array) $schema['properties'] : null; if (is_array($definedProps) && !in_array($param, array_keys($definedProps))) { throw new InvalidArgumentException( sprintf('Cannot set %s property of %s model because it doesn\'t exist in related OAS schema', $param, static::class) ); } $this->dataContainer[$param] = $value; break; case IMocker::DATA_TYPE_INTEGER: case IMocker::DATA_TYPE_NUMBER: case IMocker::DATA_TYPE_STRING: case IMocker::DATA_TYPE_BOOLEAN: case IMocker::DATA_TYPE_ARRAY: default: // scalar type and array cannot use property assignation throw new InvalidArgumentException( sprintf('Cannot set %s property of %s model because it\'s %s type. Use setData method instead', $param, static::class, $modelType) ); } } /** * Reads data from inaccessible (protected or private) or non-existing properties. * Ref @link https://www.php.net/manual/en/language.oop5.overloading.php#object.get * * @param string $param Property name. * * @throws \InvalidArgumentException When property doesn't exist in related OAS schema. * * @return mixed Property value */ public function __get(string $param) { $schema = (array) static::getOpenApiSchema(); $modelType = (array_key_exists('type', $schema)) ? $schema['type'] : null; $definedProps = (array_key_exists('properties', $schema)) ? (array) $schema['properties'] : null; if (!in_array($modelType, [null, IMocker::DATA_TYPE_OBJECT])) { // scalar type throw new InvalidArgumentException( sprintf('Cannot get %s property of %s model because getter is for object OAS type only', $param, static::class) ); } if ( is_array($definedProps) && in_array($param, array_keys($definedProps)) ) { return $this->dataContainer[$param]; } elseif ($definedProps === null) { // props are undefined return (isset($this->dataContainer[$param])) ? $this->dataContainer[$param] : null; } throw new InvalidArgumentException( sprintf('Cannot get %s property of %s model because it doesn\'t exist in related OAS schema', $param, static::class) ); } /** * Serializes the object to a value that can be serialized natively by json_encode(). * Ref @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php * * @return mixed Returns data which can be serialized by json_encode(), which is a value of any type other than a resource. */ public function jsonSerialize() { return $this->getData(); } /** * Checks if type is valid OAS data type. * * @param string|null $type Model type. * @param bool $throwException Throws InvalidArgumentException when set to true and processed type is invalid. * * @throws \InvalidArgumentException When $throwException set to TRUE. * * @return bool */ protected function validateModelType(?string $type = null, bool $throwException = true): bool { $isValid = in_array($type, static::VALID_OAS_DATA_TYPES); if ($type !== null && $isValid === false && $throwException) { throw new InvalidArgumentException( sprintf( 'Invalid OAS schema of %s model, "type" must be one of %s', static::class, implode(', ', static::VALID_OAS_DATA_TYPES) ) ); } return $isValid; } /** * Returns models namespace. * * @return string */ public static function getModelsNamespace(): string { return static::MODELS_NAMESPACE . stripslashes('\\'); } }