public static function set($data, $key, $value, $delimiter = '.', $storeType = 'array')
    {
        $nodes = static::getPathNodes($key, $delimiter);

        if (empty($nodes)) {
            return $data;
        }

        /**
         * A closure as inner function to create data store.
         *
         * @param string $type Type name.
         *
         * @return  array
         *
         * @throws \InvalidArgumentException
         */
        $createStore = function ($type) {
            if (strtolower($type) === 'array') {
                return [];
            }

            if (class_exists($type)) {
                return new $type();
            }

            throw new \InvalidArgumentException(sprintf('Type or class: %s not exists', $type));
        };

        $dataTmp = &$data;

        foreach ($nodes as $node) {
            if (is_object($dataTmp)) {
                // If this node not exists, create new one.
                if (empty($dataTmp->$node)) {
                    $dataTmp->$node = $createStore($storeType);
                }

                $dataTmp = &$dataTmp->$node;
            } elseif (is_array($dataTmp)) {
                // If this node not exists, create new one.
                if (empty($dataTmp[$node])) {
                    $dataTmp[$node] = $createStore($storeType);
                }

                $dataTmp = &$dataTmp[$node];
            } else {
                // If a node is value but path is not go to the end, we replace this value as a new store.
                // Then next node can insert new value to this store.
                $dataTmp = &$createStore($storeType);
            }
        }

        // Now, path go to the end, means we get latest node, set value to this node.
        $dataTmp = $value;

        return $data;
    }