object = $object; $this->resource_server_context = $resource_server_context; } protected static $array_mappings = []; protected static $allowed_fields = []; protected static $allowed_relations = []; /** * @return array */ protected function getAllowedFields() { $mappings = []; $hierarchy = $this->getClassHierarchy(); foreach($hierarchy as $class_name){ if($class_name === 'Libs\ModelSerializers\AbstractSerializer') continue; $class = new $class_name($this->object, $this->resource_server_context); $mappings = array_merge($mappings, $class->getSelfAllowedFields()); } $mappings = array_merge($mappings, $this->getSelfAllowedFields()); return $mappings; } private function getSelfAllowedFields(){ return static::$allowed_fields; } /** * @return array */ protected function getAllowedRelations() { $mappings = array(); $hierarchy = $this->getClassHierarchy(); foreach($hierarchy as $class_name){ if($class_name === 'Libs\ModelSerializers\AbstractSerializer') continue; $class = new $class_name($this->object, $this->resource_server_context); $mappings = array_merge($mappings, $class->getSelfAllowedRelations()); } $mappings = array_merge($mappings, $this->getSelfAllowedRelations()); return $mappings; } private function getSelfAllowedRelations(){ return static::$allowed_relations; } /** * @return array */ private function getAttributeMappings() { $mappings = []; $hierarchy = $this->getClassHierarchy(); foreach($hierarchy as $class_name){ if($class_name === 'Libs\ModelSerializers\AbstractSerializer') continue; $class = new $class_name($this->object, $this->resource_server_context); $mappings = array_merge($mappings, $class->getSelfMappings()); } $mappings = array_merge($mappings, $this->getSelfMappings()); return $mappings; } private function getSelfMappings(){ return static::$array_mappings; } /** * @return array */ private function getClassHierarchy(){ return array_reverse($this->get_class_lineage($this)); } private function get_class_lineage($object) { $class_name = get_class($object); $parents = array_values(class_parents($class_name)); return array_merge(array($class_name), $parents); } const BoolType = 'json_boolean'; const EpochType = 'datetime_epoch'; const StringType = 'json_string'; const IntType = 'json_int'; const FloatType = 'json_float'; const ObfuscatedEmailType = 'json_obfuscated_email'; const UrlType = 'json_url'; const ColorType = 'json_color'; const ValidTypes = [ self::BoolType, self::EpochType, self::StringType, self::IntType, self::FloatType, self::ObfuscatedEmailType, self::UrlType, self::ColorType, ]; /** * @param string $field * @param string $type * @return string */ public static function buildMapping(string $field, string $type):string { if(!in_array($type, self::ValidTypes)) throw new \InvalidArgumentException(); return sprintf("%s:%s", $field, $type); } protected $expand_mappings = []; /** * @param null $expand * @param array $fields * @param array $relations * @param array $params * @return array */ public function serialize($expand = null, array $fields = [], array $relations = [], array $params = []) { $values = []; $method_prefix = ['get', 'is']; if(!count($fields)) $fields = $this->getAllowedFields(); $mappings = $this->getAttributeMappings(); if (count($mappings)) { $new_values = []; foreach ($mappings as $attribute => $mapping) { $mapping = preg_split('/:/', $mapping); if(count($fields) > 0 && !in_array($mapping[0], $fields)) continue; $value = null; $method_found = false; foreach($method_prefix as $prefix){ if(method_exists($this->object, $prefix.$attribute)){ try { $value = call_user_func([$this->object, $prefix . $attribute]); $method_found = true; break; } catch (\Exception $ex){ Log::warning($ex); $value = null; } } } if(!$method_found){ try { //try dynamic one $value = call_user_func([$this->object, 'get'.$attribute ]); } catch (\Exception $ex){ Log::warning($ex); $value = null; } } if(count($mapping) > 1) { //we have a formatter ... switch(strtolower($mapping[1])) { case 'datetime_epoch': { if(!is_null($value)) { $value = $value->getTimestamp(); } } break; case 'json_string': { $value = JsonUtils::toJsonString($value); } break; case 'json_boolean': { $value = JsonUtils::toJsonBoolean($value); } break; case 'json_color': { $value = JsonUtils::toJsonColor($value); } break; case 'json_int': { $value = JsonUtils::toJsonInt($value); } break; case 'json_float': { $value = JsonUtils::toJsonFloat($value); } break; case 'json_obfuscated_email': { $value = JsonUtils::toObfuscatedEmail($value); } case 'json_url':{ $value = JsonUtils::encodeUrl($value); } break; } } $new_values[$mapping[0]] = $value; } $values = $new_values; } // expand logic if (!empty($expand)) { $exp_expand = explode(',', $expand); foreach ($exp_expand as $relation) { $relation = trim($relation); if(isset($this->expand_mappings[$relation])){ $values = $this->expand_mappings[$relation]->serialize($values, $expand); } } } return $values; } /** * @param string $expand_str * @param string $prefix * @return string */ public static function filterExpandByPrefix($expand_str, $prefix){ $expand_to = explode(',', $expand_str); $filtered_expand = array_filter($expand_to, function($element) use($prefix){ return preg_match('/^' . preg_quote($prefix, '/') . '\./', strtolower(trim($element))) > 0; }); $res = ''; foreach($filtered_expand as $filtered_expand_elem){ if(strlen($res) > 0) $res .= ','; $res .= str_replace_first($prefix.".","", strtolower(trim($filtered_expand_elem))); } return $res; } /** * @param string $prefix * @param string $expand * @return string */ protected static function getExpandForPrefix(string $prefix, string $expand):string { Log::debug(sprintf("AbstractSerializer::getExpandForPrefix prefix %s expand %s", $prefix, $expand)); $prefix_expand = []; foreach(explode(',', $expand) as $e){ if(strstr($e, $prefix.".")!==false) $prefix_expand[] = str_replace($prefix.".","", $e); } return implode(',', $prefix_expand); } }