<?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);
}