1<?php
2/**
3 * Created by PhpStorm.
4 * User: yf
5 * Date: 2018/5/22
6 * Time: 下午2:52
7 */
8
9namespace EasySwoole\Spl;
10
11
12class SplArray extends \ArrayObject
13{
14    function __get($name)
15    {
16        if (isset($this[$name])) {
17            return $this[$name];
18        } else {
19            return null;
20        }
21    }
22
23    function __set($name, $value): void
24    {
25        $this[$name] = $value;
26    }
27
28    function __toString(): string
29    {
30        return json_encode($this, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
31    }
32
33    function getArrayCopy(): array
34    {
35        return (array)$this;
36    }
37
38    function set($path, $value): void
39    {
40        $path = explode(".", $path);
41        $temp = $this;
42        while ($key = array_shift($path)) {
43            $temp = &$temp[$key];
44        }
45        $temp = $value;
46    }
47
48    function unset($path)
49    {
50        $finalKey = null;
51        $path = explode(".", $path);
52        $temp = $this;
53        while (count($path) > 1 && $key = array_shift($path)) {
54            $temp = &$temp[$key];
55        }
56        $finalKey = array_shift($path);
57        if (isset($temp[$finalKey])) {
58            unset($temp[$finalKey]);
59        }
60    }
61
62    function get($path)
63    {
64        $paths = explode(".", $path);
65        $data = $this->getArrayCopy();
66        while ($key = array_shift($paths)) {
67            if (isset($data[$key])) {
68                $data = $data[$key];
69            } else {
70                if ($key == '*') {
71                    $temp = [];
72                    if (is_array($data)) {
73                        if (!empty($paths)) {
74                            $path = implode("/", $paths);
75                        } else {
76                            $path = null;
77                        }
78                        foreach ($data as $key => $datum) {
79                            if (is_array($datum)) {
80                                $ctemp = (new SplArray($datum))->get($path);
81                                if ($ctemp !== null) {
82                                    $temp[][$path] = $ctemp;
83                                }
84                            } else if ($datum !== null) {
85                                $temp[$key] = $datum;
86                            }
87
88                        }
89                    }
90                    return $temp;
91                } else {
92                    return null;
93                }
94            }
95        }
96        return $data;
97    }
98
99    public function delete($key): void
100    {
101        $this->unset($key);
102    }
103
104    /**
105     * 数组去重取唯一的值
106     * @return SplArray
107     */
108    public function unique(): SplArray
109    {
110        return new SplArray(array_unique($this->getArrayCopy(), SORT_REGULAR));
111    }
112
113    /**
114     * 获取数组中重复的值
115     * @return SplArray
116     */
117    public function multiple(): SplArray
118    {
119        $unique_arr = array_unique($this->getArrayCopy(), SORT_REGULAR);
120        return new SplArray(array_udiff_uassoc($this->getArrayCopy(), $unique_arr, function ($key1, $key2) {
121            if ($key1 === $key2) {
122                return 0;
123            }
124            return 1;
125        }, function ($value1, $value2) {
126            if ($value1 === $value2) {
127                return 0;
128            }
129            return 1;
130        }));
131    }
132
133    /**
134     * 按照键值升序
135     * @return SplArray
136     */
137    public function asort(): SplArray
138    {
139        parent::asort();
140        return $this;
141    }
142
143    /**
144     * 按照键升序
145     * @return SplArray
146     */
147    public function ksort(): SplArray
148    {
149        parent::ksort();
150        return $this;
151    }
152
153    /**
154     * 自定义排序
155     * @param int $sort_flags
156     * @return SplArray
157     */
158    public function sort($sort_flags = SORT_REGULAR): SplArray
159    {
160        $temp = $this->getArrayCopy();
161        sort($temp, $sort_flags);
162        return new SplArray($temp);
163    }
164
165    /**
166     * 取得某一列
167     * @param string      $column
168     * @param null|string $index_key
169     * @return SplArray
170     */
171    public function column($column, $index_key = null): SplArray
172    {
173        return new SplArray(array_column($this->getArrayCopy(), $column, $index_key));
174    }
175
176    /**
177     * 交换数组中的键和值
178     * @return SplArray
179     */
180    public function flip(): SplArray
181    {
182        return new SplArray(array_flip($this->getArrayCopy()));
183    }
184
185    /**
186     * 过滤本数组
187     * @param string|array $keys 需要取得/排除的键
188     * @param bool         $exclude true则排除设置的键名 false则仅获取设置的键名
189     * @return SplArray
190     */
191    public function filter($keys, $exclude = false): SplArray
192    {
193        if (is_string($keys)) {
194            $keys = explode(',', $keys);
195        }
196        $new = array();
197        foreach ($this->getArrayCopy() as $name => $value) {
198            if (!$exclude) {
199                in_array($name, $keys) ? $new[$name] = $value : null;
200            } else {
201                in_array($name, $keys) ? null : $new[$name] = $value;
202            }
203        }
204        return new SplArray($new);
205    }
206
207
208    public function keys($path = null): array
209    {
210        if (!empty($path)) {
211            $temp = $this->get($path);
212            if (is_array($temp)) {
213                return array_keys($temp);
214            } else {
215                return [];
216            }
217        }
218        return array_keys((array)$this);
219    }
220
221    /**
222     * 提取数组中的值
223     * @return SplArray
224     */
225    public function values(): SplArray
226    {
227        return new SplArray(array_values($this->getArrayCopy()));
228    }
229
230    public function flush(): SplArray
231    {
232        foreach ($this->getArrayCopy() as $key => $item) {
233            unset($this[$key]);
234        }
235        return $this;
236    }
237
238    public function loadArray(array $data)
239    {
240        parent::__construct($data);
241        return $this;
242    }
243
244    function merge(array $data)
245    {
246        return $this->loadArray($data + $this->getArrayCopy());
247    }
248
249    /*
250     $test = new \EasySwoole\Spl\SplArray([
251        'title'=>'title',
252        'items'=>[
253            ['title'=>'Some string', 'number' => 1],
254            ['title'=>'Some string', 'number' => 2],
255            ['title'=>'Some string', 'number' => 3]
256        ]
257    ]);
258     */
259    public function toXML($CD_DATA = false, $rootName = 'xml', $encoding = 'UTF-8', $item = 'item')
260    {
261        $data = $this->getArrayCopy();
262        if ($CD_DATA) {
263            /*
264             * 默认制定
265             */
266            $xml = new class('<?xml version="1.0" encoding="' . $encoding . '" ?>' . "<{$rootName}></{$rootName}>") extends \SimpleXMLElement
267            {
268                public function addCData($cdata_text)
269                {
270                    $dom = dom_import_simplexml($this);
271                    $cdata = $dom->ownerDocument->createCDATASection($cdata_text);
272                    $dom->appendChild($cdata);
273                }
274            };
275        } else {
276            $xml = new \SimpleXMLElement('<?xml version="1.0" encoding="' . $encoding . '" ?>' . "<{$rootName} ></{$rootName}>");
277        }
278        $parser = function ($xml, $data) use (&$parser, $CD_DATA, $item) {
279            foreach ($data as $k => $v) {
280                if (is_array($v)) {
281                    if (!is_numeric($k)) {
282                        $ch = $xml->addChild($k);
283                    } else {
284                        $ch = $xml->addChild($item);
285                    }
286                    $parser($ch, $v);
287                } else {
288                    if (is_numeric($k)) {
289                        $xml->addChild($k, $v);
290                    } else {
291                        if ($CD_DATA) {
292                            $n = $xml->addChild($k);
293                            $n->addCData($v);
294                        } else {
295                            $xml->addChild($k, $v);
296                        }
297                    }
298                }
299            }
300        };
301        $parser($xml, $data);
302        unset($parser);
303        $str = $xml->asXML();
304        return substr($str, strpos($str, "\n") + 1);
305    }
306
307}
308