1. 程式人生 > 實用技巧 >陣列轉物件、物件轉陣列

陣列轉物件、物件轉陣列

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


}