陣列轉物件、物件轉陣列
阿新 • • 發佈:2020-08-06
<?php namespace App\Utils { class BeanUtils { const TYPE_INT = 'int'; const TYPE_STRING = 'string'; const TYPE_FLOAT = 'float'; const TYPE_DEFAULT = '-'; protected static $studlyCache = []; protected static $camelCache = []; protected static $snakeCache = []; protected static $classCache = []; /** * @param object $object * @param bool $snake * @return array * @throws \Exception */ public static function toArray($object, $snake = true) { if (!is_object($object)) { throw new \Exception('target只支援物件'); } $data = []; $propertiesMap = self::parseProperties($object); foreach ($propertiesMap as $name => $propertyInfo) { $typeName = $propertyInfo['type']; $property = $propertyInfo['property']; if ($snake) { $nameShow = self::snake($name); } else { $nameShow = $name; } $value = null; switch ($typeName) { case self::TYPE_INT: $value = $property->getValue($object); $value = intval($value); break; case self::TYPE_FLOAT: $value = $property->getValue($object); $value = floatval($value); break; case self::TYPE_STRING: $value = $property->getValue($object); $value = strval($value); break; case self::TYPE_DEFAULT: $value = $property->getValue($object); break; default: if (class_exists($typeName)) { $objTemp = $property->getValue($object); $value = self::toArray($objTemp, $snake); } else { $value = $property->getValue($object); } } $data[$nameShow] = $value; } return $data; } /** * @param array $source * @param object $target * @throws Exception */ public static function toObject($source, $target) { if (!is_object($target)) { throw new \Exception('target只支援物件'); } $propertiesMap = self::parseProperties($target); if (is_array($source)) { foreach ($source as $key => $value) { $keyStudly = self::camel($key); if (!isset($propertiesMap[$keyStudly])) { continue; } $propertyInfo = $propertiesMap[$keyStudly]; $typeName = $propertyInfo['type']; $property = $propertyInfo['property']; $property->setAccessible(true); switch ($typeName) { case self::TYPE_INT: $property->setValue($target, intval($value)); break; case self::TYPE_FLOAT: $property->setValue($target, floatval($value)); break; case self::TYPE_STRING: $property->setValue($target, strval($value)); break; case self::TYPE_DEFAULT: $property->setValue($target, $value); break; default: if (class_exists($typeName)) { $objTemp = new $typeName(); self::toObject($value, $objTemp); $property->setValue($target, $objTemp); } else { $property->setValue($target, $value); } } } } } public static function studly($value) { $key = $value; if (isset(static::$studlyCache[$key])) { return static::$studlyCache[$key]; } $value = ucwords(str_replace(['-', '_'], ' ', $value)); return static::$studlyCache[$key] = str_replace(' ', '', $value); } /** * 轉成駝峰 * @param $value * @return mixed|string */ public static function camel($value) { if (isset(static::$camelCache[$value])) { return static::$camelCache[$value]; } return static::$camelCache[$value] = lcfirst(static::studly($value)); } /** * 轉成蛇形寫法 * @param $value * @param string $delimiter * @return bool|false|mixed|string|string[]|null */ public static function snake($value, $delimiter = '_') { $key = $value; if (isset(static::$snakeCache[$key][$delimiter])) { return static::$snakeCache[$key][$delimiter]; } if (!ctype_lower($value)) { $value = preg_replace('/\s+/u', '', ucwords($value)); $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $value)); } return static::$snakeCache[$key][$delimiter] = $value; } public static function lower($value) { return mb_strtolower($value, 'UTF-8'); } /** * 解析類屬性 * @param $target * @return array|mixed * @throws ReflectionException */ private static function parseProperties($target) { $reflectionClass = new \ReflectionClass($target); $className = $reflectionClass->getName(); if (isset(static::$classCache[$className])) { return static::$classCache[$className]; } $properties = $reflectionClass->getProperties(); $propertiesMap = []; foreach ($properties as $property) { $name = $property->getName(); $comment = $property->getDocComment(); preg_match('/[\s]*@var[\s]+(.*)?\n/', $comment, $matches); if (empty($matches[1])) { $propertiesMap[$name] = [ 'property' => $property, 'type' => self::TYPE_DEFAULT, ]; continue; } $typeNameRaw = trim($matches[1]); if (empty($typeNameRaw)) { $propertiesMap[$name] = [ 'property' => $property, 'type' => self::TYPE_DEFAULT, ]; continue; } $typeName = strtoupper($typeNameRaw); switch ($typeName) { case 'STRING': $propertiesMap[$name] = [ 'property' => $property, 'type' => self::TYPE_STRING, ]; break; case 'INT': $propertiesMap[$name] = [ 'property' => $property, 'type' => self::TYPE_INT, ]; break; case 'FLOAT': $propertiesMap[$name] = [ 'property' => $property, 'type' => self::TYPE_FLOAT, ]; break; default: if (class_exists($typeNameRaw, false)) { $propertiesMap[$name] = [ 'property' => $property, 'type' => $typeNameRaw, ]; } else { $propertiesMap[$name] = [ 'property' => $property, 'type' => self::TYPE_DEFAULT, ]; } } } static::$classCache[$className] = $propertiesMap; return $propertiesMap; } } } namespace App\Entity\User { class User { /** * b * @var int * a */ public $userId = 0; /** * @var string */ public $userName = ''; /** * @var float */ public $money = 0.0; /** * @var App\Entity\User\UserExt */ public $userExt; } class UserExt { /** * @var int */ public $age = 0; /** * MALE 或者 FEMALE * @var string */ public $sex = 'MALE'; } } namespace { use App\Utils\BeanUtils; use App\Entity\User\User; $userInfo = [ 'user_id' => 10, 'user_name' => 'name_10', 'money' => 1.0, 'user_ext' => [ 'age' => 100, 'sex' => 'MALE' ] ]; $userObj = new User(); BeanUtils::toObject($userInfo, $userObj); var_dump($userObj); $userInfoRes = BeanUtils::toArray($userObj); var_dump($userInfoRes); }