├── .github
└── workflows
│ └── php.yml
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── composer.json
├── docs
├── changelog.md
└── todo.md
├── src
├── Concurrent
│ ├── CallableWrapper.php
│ ├── Future.php
│ ├── Promise.php
│ ├── Wrapper.php
│ └── functions.php
├── Consts.php
├── Exceptions
│ ├── BaseException.php
│ └── UncatchableException.php
├── Helpers
│ ├── ArrayHelper.php
│ ├── ConvertHelper.php
│ ├── DateHelper.php
│ ├── DebugHelper.php
│ ├── DirectoryHelper.php
│ ├── EncryptHelper.php
│ ├── FileHelper.php
│ ├── NumberHelper.php
│ ├── OsHelper.php
│ ├── RegularHelper.php
│ ├── StringHelper.php
│ ├── UrlHelper.php
│ └── ValidateHelper.php
├── Interfaces
│ ├── Arrayable.php
│ ├── Jsonable.php
│ └── Throwable.php
├── Objects
│ ├── ArrayObject.php
│ ├── BaseObject.php
│ └── StrictObject.php
├── Services
│ └── BaseService.php
├── Util
│ └── MacAddress.php
└── Version.php
└── tests
├── Future
├── .gitkeep
├── ExceptionFuture.php
├── FailFuture.php
├── MyGenerator.php
├── SuccessFuture.php
└── UncatchableFuture.php
├── Objects
├── BaseCls.php
├── BaseServ.php
├── MathCls.php
└── StrictCls.php
├── README.md
├── Unit
├── ArrayHelperTest.php
├── ConvertHelperTest.php
├── DateHelperTest.php
├── DebugHelperTest.php
├── DirectoryHelperTest.php
├── EncryptHelperTest.php
├── FileHelperTest.php
├── MacAddressTest.php
├── NumberHelperTest.php
├── ObjectsTest.php
├── OsHelperTest.php
├── PromiseTest.php
├── ServicesTest.php
├── StringHelperTest.php
├── UrlHelperTest.php
└── ValidateHelperTest.php
├── bootstrap.php
├── data
├── banana.gif
├── bom.txt
├── green.jpg
├── php-logo.svg
├── php_elephant.png
└── png.webp
├── phpunit.xml
└── tmp
└── .gitkeep
/.github/workflows/php.yml:
--------------------------------------------------------------------------------
1 | name: helper-test
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | build-test:
11 | runs-on: ${{ matrix.os }}
12 | env:
13 | PHP_EXTENSIONS: curl, dom, json, libxml, mbstring, xml, xmlwriter
14 | PHP_INI_VALUES: memory_limit=-1, error_reporting=-1, log_errors_max_len=0, display_errors=On
15 |
16 | strategy:
17 | # 策略组中,如果有一个失败了,则快速停止继续运行
18 | fail-fast: false
19 | matrix:
20 | os:
21 | - ubuntu-latest
22 | #- windows-latest
23 |
24 | php-version:
25 | - "7.2"
26 | - "7.3"
27 | - "7.4"
28 | - "8.0"
29 | - "8.1"
30 | - "8.2"
31 |
32 | steps:
33 | - name: Checkout
34 | uses: actions/checkout@v3
35 |
36 | - name: Install PHP
37 | uses: shivammathur/setup-php@v2
38 | with:
39 | php-version: ${{ matrix.php-version }}
40 | coverage: none
41 | extensions: ${{ env.PHP_EXTENSIONS }}
42 | ini-values: ${{ env.PHP_INI_VALUES }}
43 | tools: composer
44 |
45 | - name: Update dependencies with composer
46 | run: composer update --no-ansi --no-interaction --no-progress
47 |
48 | - name: Run tests with phpunit
49 | run: vendor/bin/phpunit --version && vendor/bin/phpunit --bootstrap=tests/bootstrap.php ./tests/
50 |
51 | - name: Show info
52 | run: ls -a -h ./ && ls -a -h ./tests
53 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | *.log
3 | vendor
4 | composer.lock
5 | .vscode
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 7.2
5 | - 7.3
6 | - 7.4
7 | - 8.0
8 |
9 | os:
10 | - linux
11 |
12 | before_script:
13 | - travis_retry composer self-update
14 | - travis_retry composer install --no-interaction --prefer-source --dev
15 | - cd tests
16 |
17 | script:
18 | - ../vendor/bin/phpunit --coverage-clover=coverage.xml
19 |
20 | after_success:
21 | - bash <(curl -s https://codecov.io/bash)
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # php-helper
2 |
3 | k's php helper/library/utils
4 | php 常用函数库/工具集,包括数组、文件、验证、字符串、加解密等操作,具体见`src/Helpers`.
5 | 支持的php版本有:
6 |
7 | - 7.2
8 | - 7.3
9 | - 7.4
10 | - 8.0
11 | - 8.1
12 | - 8.2
13 |
14 | ### 相关
15 |
16 | [](https://secure.php.net/)
17 | [](https://github.com/kakuilan/php-helper/actions)
18 | [](https://codecov.io/gh/kakuilan/php-helper)
19 | [](https://github.com/kakuilan/php-helper)
20 | [](https://github.com/kakuilan/php-helper)
21 | [](https://packagist.org/packages/kakuilan/php-helper)
22 |
23 | ### 安装
24 |
25 | ```shell
26 | composer require kakuilan/php-helper
27 | ```
28 |
29 | ### 测试
30 |
31 | ```sh
32 | phpunit --bootstrap=tests/bootstrap.php ./tests/
33 | composer run-script test
34 | composer run-script cover
35 | # 或者
36 | cd tests
37 | phpunit
38 | ```
39 |
40 | ### 更新日志
41 |
42 | 详见[[Changelog]](/docs/changelog.md)
43 |
44 |
45 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kakuilan/php-helper",
3 | "description": "k`s php helper/library/utils",
4 | "keywords": [
5 | "php helper",
6 | "php library",
7 | "php utils"
8 | ],
9 | "homepage": "https://github.com/kakuilan/php-helper",
10 | "license": "Apache-2.0",
11 | "authors": [
12 | {
13 | "name": "kakuilan",
14 | "email": "kakuilan@163.com",
15 | "homepage": "https://github.com/kakuilan"
16 | }
17 | ],
18 | "require": {
19 | "php": ">=7.2",
20 | "ext-curl": "*",
21 | "ext-dom": "*",
22 | "ext-json": "*",
23 | "ext-mbstring": "*"
24 | },
25 | "require-dev": {
26 | "phpunit/phpunit": "*"
27 | },
28 | "autoload": {
29 | "psr-4": {
30 | "Kph\\": "src",
31 | "Kph\\Tests\\": "tests"
32 | },
33 | "files": [
34 | "src/Concurrent/functions.php"
35 | ]
36 | },
37 | "scripts": {
38 | "test": "phpunit --bootstrap=tests/bootstrap.php ./tests/",
39 | "cover": "phpunit --bootstrap=tests/bootstrap.php --coverage-clover=coverage.xml ./tests/"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/docs/changelog.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | ## [v0.5.3]- 2024-05-06
6 |
7 | #### Added
8 |
9 | - none
10 |
11 | #### Fixed
12 |
13 | - none
14 |
15 | #### Changed
16 |
17 | - 优化`EncryptHelper::authcode`
18 |
19 | #### Removed
20 |
21 | - none
22 |
23 | ## [v0.5.2]- 2023-06-25
24 |
25 | #### Added
26 |
27 | - none
28 |
29 | #### Fixed
30 |
31 | - 修复`UrlHelper::getUrl`
32 |
33 | #### Changed
34 |
35 | - none
36 |
37 | #### Removed
38 |
39 | - none
40 |
41 | ## [v0.5.1]- 2023-06-20
42 |
43 | #### Added
44 |
45 | - none
46 |
47 | #### Fixed
48 |
49 | - 修复`UrlHelper::getUrl`
50 | - 修复`ValidateHelper::isUrl`
51 |
52 | #### Changed
53 |
54 | - none
55 |
56 | #### Removed
57 |
58 | - none
59 |
60 | ## [v0.5.0]- 2023-06-16
61 |
62 | #### Added
63 |
64 | - none
65 |
66 | #### Fixed
67 |
68 | - none
69 |
70 | #### Changed
71 |
72 | - 修改`BaseService::getResult`,取消类型限制
73 |
74 | #### Removed
75 |
76 | - none
77 |
78 | ## [v0.4.9]- 2023-06-15
79 |
80 | #### Added
81 |
82 | - none
83 |
84 | #### Fixed
85 |
86 | - none
87 |
88 | #### Changed
89 |
90 | - 修改`BaseService::$result`类型为mixed
91 |
92 | #### Removed
93 |
94 | - none
95 |
96 | ## [v0.4.8]- 2023-06-06
97 |
98 | #### Added
99 |
100 | - none
101 |
102 | #### Fixed
103 |
104 | - 修复`StrictObject::isset`
105 |
106 | #### Changed
107 |
108 | - 优化`DateHelper::timestamp`
109 | - 优化`DateHelper::isBetween`
110 | - 优化`StringHelper::passwdSafeGrade`
111 |
112 | #### Removed
113 |
114 | - 移除`DateHelper::dateTime`
115 | - 移除`DateHelper::yearMonth`
116 | - 移除`DateHelper::monthDay`
117 |
118 | ## [v0.4.7]- 2023-05-04
119 |
120 | #### Added
121 |
122 | - 新增`ArrayHelper::xmlToArray`
123 | - 新增`ArrayHelper::arrayToXml`
124 | - 新增`DateHelper::timestamp`
125 | - 新增`DateHelper::year`
126 | - 新增`DateHelper::month`
127 | - 新增`DateHelper::day`
128 | - 新增`DateHelper::hour`
129 | - 新增`DateHelper::minute`
130 | - 新增`DateHelper::second`
131 | - 新增`DateHelper::yearMonth`
132 | - 新增`DateHelper::monthDay`
133 | - 新增`DateHelper::format`
134 | - 新增`DateHelper::dateTime`
135 | - 新增`DateHelper::isBetween`
136 |
137 | #### Fixed
138 |
139 | - none
140 |
141 | #### Changed
142 |
143 | - none
144 |
145 | #### Removed
146 |
147 | - none
148 |
149 | ## [v0.4.6]- 2023-02-28
150 |
151 | #### Added
152 |
153 | - 新增`BaseService::setResult`
154 | - 新增`BaseService::getResult`
155 |
156 | #### Fixed
157 |
158 | - none
159 |
160 | #### Changed
161 |
162 | - none
163 |
164 | #### Removed
165 |
166 | - none
167 |
168 | ## [v0.4.5]- 2022-04-01
169 |
170 | #### Added
171 |
172 | - none
173 |
174 | #### Fixed
175 |
176 | - 优化`EncryptHelper::authcode`
177 |
178 | #### Changed
179 |
180 | - none
181 |
182 | #### Removed
183 |
184 | - none
185 |
186 | ## [v0.4.4]- 2022-01-14
187 |
188 | #### Added
189 |
190 | - 新增`UrlHelper::getSiteUrl`,根据网址获取站点URL
191 |
192 | #### Fixed
193 |
194 | - none
195 |
196 | #### Changed
197 |
198 | - 将`OsHelper::getDomain`移动到`UrlHelper::getDomain`
199 | - 将`OsHelper::getUrl`移动到`UrlHelper::getUrl`
200 | - 将`OsHelper::getUri`移动到`UrlHelper::getUri`
201 |
202 | #### Removed
203 |
204 | - none
205 |
206 | ## [v0.4.3]- 2022-01-13
207 |
208 | #### Added
209 |
210 | - none
211 |
212 | #### Fixed
213 |
214 | - none
215 |
216 | #### Changed
217 |
218 | - 兼容php 8.0
219 |
220 | #### Removed
221 |
222 | - none
223 |
224 | ## [v0.4.2]- 2021-12-09
225 |
226 | #### Added
227 |
228 | - none
229 |
230 | #### Fixed
231 |
232 | - none
233 |
234 | #### Changed
235 |
236 | - 修改常量`DELIMITER`
237 |
238 | #### Removed
239 |
240 | - none
241 |
242 | ## [v0.4.1]- 2021-10-24
243 |
244 | #### Added
245 |
246 | - 增加`StringHelper::toBytes`
247 | - 增加`StringHelper::bytes2Str`
248 |
249 | #### Fixed
250 |
251 | - none
252 |
253 | #### Changed
254 |
255 | - none
256 |
257 | #### Removed
258 |
259 | - none
260 |
261 | ## [v0.4.0]- 2021-04-13
262 |
263 | #### Added
264 |
265 | - 增加`ValidateHelper::startsWiths`
266 | - 增加`ValidateHelper::endsWiths`
267 |
268 | #### Fixed
269 |
270 | - none
271 |
272 | #### Changed
273 |
274 | - none
275 |
276 | #### Removed
277 |
278 | - none
279 |
280 | ## [v0.3.9]- 2021-02-23
281 |
282 | #### Added
283 |
284 | - 增加`OsHelper::remoteFileExists`
285 |
286 | #### Fixed
287 |
288 | - none
289 |
290 | #### Changed
291 |
292 | - none
293 |
294 | #### Removed
295 |
296 | - none
297 |
298 | ## [v0.3.8]- 2021-01-11
299 |
300 | #### Added
301 |
302 | - 增加`OsHelper::isSsl`
303 |
304 | #### Fixed
305 |
306 | - none
307 |
308 | #### Changed
309 |
310 | - none
311 |
312 | #### Removed
313 |
314 | - none
315 |
316 | ## [v0.3.7]- 2021-01-05
317 |
318 | #### Added
319 |
320 | - 增加`OsHelper::isAjax`
321 |
322 | #### Fixed
323 |
324 | - none
325 |
326 | #### Changed
327 |
328 | - none
329 |
330 | #### Removed
331 |
332 | - none
333 |
334 | ## [v0.3.6]- 2020-12-19
335 |
336 | #### Added
337 |
338 | - none
339 |
340 | #### Fixed
341 |
342 | - none
343 |
344 | #### Changed
345 |
346 | - 修改`ArrayHelper::searchItem`支持可迭代的对象
347 | - 修改`ArrayHelper::searchMutil`支持可迭代的对象
348 |
349 | #### Removed
350 |
351 | - none
352 |
353 | ## [v0.3.5]- 2020-12-18
354 |
355 | #### Added
356 |
357 | - none
358 |
359 | #### Fixed
360 |
361 | - none
362 |
363 | #### Changed
364 |
365 | - 修改`ArrayHelper::searchItem`支持数组元素是对象
366 | - 修改`ArrayHelper::searchMutil`支持数组元素是对象
367 |
368 | #### Removed
369 |
370 | - none
371 |
372 | ## [v0.3.4]- 2020-12-10
373 |
374 | #### Added
375 |
376 | - 添加`OsHelper::runCommand`
377 | - 添加`ValidateHelper::isMacAddress`
378 | - 添加`Kph\Util\MacAddress`
379 |
380 | #### Fixed
381 |
382 | - none
383 |
384 | #### Changed
385 |
386 | - 修改`DebugHelper::errorLogHandler`临时目录
387 | - 修改`DirectoryHelper::formatDir`,兼容windows路径
388 | - 修改`OsHelper::getPhpPath`,兼容windows
389 |
390 | #### Removed
391 |
392 | - none
393 |
394 | ## [v0.3.3]- 2020-12-8
395 |
396 | #### Added
397 |
398 | - none
399 |
400 | #### Fixed
401 |
402 | - none
403 |
404 | #### Changed
405 |
406 | - 规范BaseService错误信息类型
407 |
408 | #### Removed
409 |
410 | - none
411 |
412 | ## [v0.3.2]- 2020-12-2
413 |
414 | #### Added
415 |
416 | - 方法`EncryptHelper::opensslEncrypt`
417 | - 方法`EncryptHelper::opensslDecrypt`
418 |
419 | #### Fixed
420 |
421 | - none
422 |
423 | #### Changed
424 |
425 | - none
426 |
427 | #### Removed
428 |
429 | - none
430 |
431 | ## [v0.3.1]- 2020-11-25
432 |
433 | #### Added
434 |
435 | - 方法`ConvertHelper::hex2Str`
436 | - 方法`ConvertHelper::str2hex`
437 |
438 | #### Fixed
439 |
440 | - none
441 |
442 | #### Changed
443 |
444 | - move `ArrayHelper::array2Object` to `ConvertHelper::array2Object`
445 | - move `ArrayHelper::object2Array` to `ConvertHelper::object2Array`
446 |
447 | #### Removed
448 |
449 | - none
450 |
451 | ## [v0.3.0]- 2020-11-21
452 |
453 | #### Added
454 |
455 | - 方法`NumberHelper::money2Yuan`
456 | - 方法`NumberHelper::nearLogarithm`
457 | - 方法`NumberHelper::splitNaturalNum`
458 | - 方法`OsHelper::getOS`
459 | - 方法`OsHelper::isMac`
460 | - 方法`StringHelper::grabBrackets`
461 | - 方法`StringHelper::stripBrackets`
462 | - 方法`StringHelper::toArray`
463 |
464 | #### Fixed
465 |
466 | - 修改`BaseService::getError`为null时类型错误
467 |
468 | #### Changed
469 |
470 | - 修改`FileHelper::img2Base64`,增加图片类型
471 | - 修改`StringHelper::dstrpos`,使用mb_strpos
472 |
473 | #### Removed
474 |
475 | - none
476 |
477 | ## [v0.2.9]- 2020-11-16
478 |
479 | #### Added
480 |
481 | - 方法`FileHelper::formatPath`
482 | - 方法`FileHelper::getAbsPath`
483 | - 方法`FileHelper::getRelativePath`
484 | - 方法`ValidateHelper::isNaturalRange`
485 |
486 | #### Fixed
487 |
488 | - none
489 |
490 | #### Changed
491 |
492 | - 修改`DirectoryHelper::formatDir`,允许特殊字符
493 |
494 | #### Removed
495 |
496 | - none
497 |
498 | ## [v0.2.8]- 2020-10-31
499 |
500 | #### Added
501 |
502 | - 方法`DateHelper::startOfHour`
503 | - 方法`DateHelper::endOfHour`
504 |
505 | #### Fixed
506 |
507 | - none
508 |
509 | #### Changed
510 |
511 | - none
512 |
513 | #### Removed
514 |
515 | - none
516 |
517 | ## [v0.2.7]- 2020-10-31
518 |
519 | #### Added
520 |
521 | - none
522 |
523 | #### Fixed
524 |
525 | - 修复`ArrayHelper::object2Array`当对象内嵌对象时不转换问题
526 |
527 | #### Changed
528 |
529 | - none
530 |
531 | #### Removed
532 |
533 | - none
534 |
535 | ## [v0.2.6]- 2020-10-19
536 |
537 | #### Added
538 |
539 | - 新增`NumberHelper::numberSub`数值截取方法
540 |
541 | #### Fixed
542 |
543 | - none
544 |
545 | #### Changed
546 |
547 | - 修改`NumberHelper::numberFormat`,去掉参数`$decPoint`和`$thousandssep`
548 |
549 | #### Removed
550 |
551 | - none
552 |
553 | ## [v0.2.5]- 2020-10-15
554 |
555 | #### Added
556 |
557 | - none
558 |
559 | #### Fixed
560 |
561 | - none
562 |
563 | #### Changed
564 |
565 | - 修改`DirectoryHelper::getFileTree`,弃用`glob`函数
566 |
567 | #### Removed
568 |
569 | - none
570 |
571 | ## [v0.2.4]- 2020-09-25
572 |
573 | #### Added
574 |
575 | - none
576 |
577 | #### Fixed
578 |
579 | - none
580 |
581 | #### Changed
582 |
583 | - 修改`ArrayHelper::regularSort`,增加参数`$recursive`是否递归
584 |
585 | #### Removed
586 |
587 | - none
588 |
589 | ## [v0.2.2]- 2020-09-22
590 |
591 | #### Added
592 |
593 | - none
594 |
595 | #### Fixed
596 |
597 | - none
598 |
599 | #### Changed
600 |
601 | - 修改`ArrayHelper::cutItems`,增加参数`$keepKey`是否保留键名
602 |
603 | #### Removed
604 |
605 | - none
606 |
607 | ## [v0.2.1]- 2020-09-17
608 |
609 | #### Added
610 |
611 | - none
612 |
613 | #### Fixed
614 |
615 | - 修复`Kph\Concurrent\makeClosureFun`
616 |
617 | #### Changed
618 |
619 | - none
620 |
621 | #### Removed
622 |
623 | - none
624 |
625 | ## [v0.2.0]- 2020-06-20
626 |
627 | #### Added
628 |
629 | - none
630 |
631 | #### Fixed
632 |
633 | - none
634 |
635 | #### Changed
636 |
637 | - 优化`EncryptHelper::authcode`
638 |
639 | #### Removed
640 |
641 | - none
642 |
643 | ## [v0.1.9]- 2020-05-20
644 |
645 | #### Added
646 |
647 | - none
648 |
649 | #### Fixed
650 |
651 | - 修复`StringHelper::toCamelCase`当输入首字母大写的驼峰字符串失败问题
652 |
653 | #### Changed
654 |
655 | - none
656 |
657 | #### Removed
658 |
659 | - none
660 |
661 | ## [v0.1.8]- 2020-05-19
662 |
663 | #### Added
664 |
665 | - none
666 |
667 | #### Fixed
668 |
669 | - none
670 |
671 | #### Changed
672 |
673 | - 方法`ValidateHelper::isIndexArray`参数不限制类型
674 | - 方法`ValidateHelper::isAssocArray`参数不限制类型
675 | - 方法`ValidateHelper::isOneDimensionalArray`参数不限制类型
676 |
677 | #### Removed
678 |
679 | - none
680 |
681 | ## [v0.1.7]- 2020-05-18
682 |
683 | #### Added
684 |
685 | - 方法`ValidateHelper::isOneDimensionalArray`
686 |
687 | #### Fixed
688 |
689 | - none
690 |
691 | #### Changed
692 |
693 | - none
694 |
695 | #### Removed
696 |
697 | - none
698 |
699 | ## [v0.1.6]- 2020-05-18
700 |
701 | #### Added
702 |
703 | - none
704 |
705 | #### Fixed
706 |
707 | - 修复`BaseObject::getClassMethods`当$filter为null时,在php7.2下失败的问题
708 |
709 | #### Changed
710 |
711 | - none
712 |
713 | #### Removed
714 |
715 | - none
716 |
717 | ## [v0.1.5]- 2020-05-18
718 |
719 | #### Added
720 |
721 | - 方法`ArrayHelper::regularSort`
722 | - 方法`BaseObject::formatNamespace`
723 | - 方法`BaseObject::getClass`
724 | - 方法`BaseObject::getClassMethods`
725 | - 方法`ValidateHelper::isEqualArray`
726 |
727 | #### Fixed
728 |
729 | - 修复`ValidateHelper::isIndexArray`存在负数索引时的问题
730 |
731 | #### Changed
732 |
733 | - none
734 |
735 | #### Removed
736 |
737 | - none
738 |
739 | ## [v0.1.4]- 2020-05-17
740 |
741 | #### Added
742 |
743 | - 方法`NumberHelper::randFloat`
744 |
745 | #### Fixed
746 |
747 | - none
748 |
749 | #### Changed
750 |
751 | - none
752 |
753 | #### Removed
754 |
755 | - none
756 |
757 | ## [v0.1.3]- 2020-05-16
758 |
759 | #### Added
760 |
761 | - 方法`ValidateHelper::isAssocArray`
762 | - 方法`ValidateHelper::isIndexArray`
763 | - 方法`ArrayHelper::compareSchema`
764 |
765 | #### Fixed
766 |
767 | - none
768 |
769 | #### Changed
770 |
771 | - none
772 |
773 | #### Removed
774 |
775 | - none
776 |
777 | ## [v0.1.2]- 2020-05-08
778 |
779 | #### Added
780 |
781 | - 方法`DateHelper::startOfDay`
782 | - 方法`DateHelper::endOfDay`
783 | - 方法`DateHelper::startOfMonth`
784 | - 方法`DateHelper::endOfMonth`
785 | - 方法`DateHelper::startOfYear`
786 | - 方法`DateHelper::endOfYear`
787 | - 方法`DateHelper::startOfWeek`
788 | - 方法`DateHelper::endOfWeek`
789 |
790 | #### Fixed
791 |
792 | - none
793 |
794 | #### Changed
795 |
796 | - none
797 |
798 | #### Removed
799 |
800 | - none
801 |
802 | ## [v0.1.1]- 2020-04-28
803 |
804 | #### Added
805 |
806 | - none
807 |
808 | #### Fixed
809 |
810 | - 修复`ValidateHelper::isNaturalNum`为0时错误
811 |
812 | #### Changed
813 |
814 | - none
815 |
816 | #### Removed
817 |
818 | - none
819 |
820 | ## [v0.1.0]- 2020-04-09
821 |
822 | #### Added
823 |
824 | - 方法`NumberHelper::numberForma`
825 | - 方法`OsHelper::isCliMode`
826 | - 方法`StringHelper::contains`
827 | - 方法`StringHelper::middle`
828 | - 方法`StringHelper::uuidV4`
829 | - 方法`ValidateHelper::isAlpha`
830 | - 方法`ValidateHelper::isAlphaChinese`
831 | - 方法`ValidateHelper::isAlphaNum`
832 | - 方法`ValidateHelper::isAlphaNumChinese`
833 | - 方法`ValidateHelper::isAlphaNumDash`
834 | - 方法`ValidateHelper::isAlphaNumDashChinese`
835 |
836 | #### Fixed
837 |
838 | - none
839 |
840 | #### Changed
841 |
842 | - none
843 |
844 | #### Removed
845 |
846 | - none
847 |
848 | ## [v0.0.8]- 2020-03-12
849 |
850 | #### Added
851 |
852 | - 方法`ArrayHelper::setDotKey`
853 | - 方法`ArrayHelper::getDotKey`
854 | - 方法`ArrayHelper::hasDotKey`
855 |
856 | #### Fixed
857 |
858 | - none
859 |
860 | #### Changed
861 |
862 | - none
863 |
864 | #### Removed
865 |
866 | - none
867 |
868 | ## [v0.0.7]- 2020-03-11
869 |
870 | #### Added
871 |
872 | - 方法`ValidateHelper::isQQ`
873 | - 方法`ValidateHelper::isNaturalNum`
874 | - 方法`StringHelper::passwdSafeGrade`
875 |
876 | #### Fixed
877 |
878 | - none
879 |
880 | #### Changed
881 |
882 | - none
883 |
884 | #### Removed
885 |
886 | - none
887 |
888 | ## [v0.0.6]- 2020-03-10
889 |
890 | #### Added
891 |
892 | - 方法`BaseObject::parseNamespacePath`
893 | - 方法`BaseObject::getShortName`
894 | - 方法`BaseObject::getNamespaceName`
895 |
896 | #### Fixed
897 |
898 | - none
899 |
900 | #### Changed
901 |
902 | - none
903 |
904 | #### Removed
905 |
906 | - 方法`BaseObject::getClassShortName`
907 |
908 | ## [v0.0.5]- 2020-03-10
909 |
910 | #### Added
911 |
912 | - 常量`Consts::DELIMITER`
913 | - 常量`Consts::PAAMAYIM_NEKUDOTAYIM`
914 |
915 | #### Fixed
916 |
917 | - none
918 |
919 | #### Changed
920 |
921 | - none
922 |
923 | #### Removed
924 |
925 | - none
926 |
927 | ## [v0.0.4]- 2020-03-09
928 |
929 | #### Added
930 |
931 | - 方法`ValidateHelper::isMultibyte`
932 | - 类`Kph\Exceptions\BaseException`
933 | - 类`Kph\Exceptions\UncatchableException`
934 |
935 | #### Fixed
936 |
937 | - 修复`StringHelper::fixHtml`BUG,使用DOMDocument替代正则
938 | - 修复`Concurrent\co`错误捕获
939 |
940 | #### Changed
941 |
942 | - rename `DirectoryHelper::emptyDir` to `DirectoryHelper::clearDir`
943 | - 修改`Future::resolve`,支持处理value是is_callable的情况
944 |
945 | #### Removed
946 |
947 | - `Kph\Concurrent\Exception\UncatchableException`
948 |
949 | ## [v0.0.3]- 2020-03-06
950 |
951 | #### Added
952 |
953 | - 方法`StringHelper::trim`
954 | - 方法`StringHelper::toCamelCase`
955 | - 方法`StringHelper::toSnakeCase`
956 | - 方法`StringHelper::toKebabCase`
957 | - 方法`StringHelper::removeBefore`
958 | - 方法`StringHelper::removeAfter`
959 | - 方法`ValidateHelper::isSpace`
960 | - 方法`ValidateHelper::isWhitespace`
961 |
962 | #### Fixed
963 |
964 | - none
965 |
966 | #### Changed
967 |
968 | - `ArrayHelper::dstrpos` 改为`StringHelper::dstrpos`
969 | - `StringHelper::removeSpace` 增加参数$all
970 | - `ValidateHelper::startsWith` 增加参数$ignoreCase
971 | - `ValidateHelper::endsWith` 增加参数$ignoreCase
972 |
973 | #### Removed
974 |
975 | - none
976 |
977 | ## [v0.0.2]- 2020-03-03
978 |
979 | #### Added
980 |
981 | - 方法`NumberHelper::geoDistance`
982 | - 方法`StringHelper::removeEmoji`
983 |
984 | #### Fixed
985 |
986 | - none
987 |
988 | #### Changed
989 |
990 | - none
991 |
992 | #### Removed
993 |
994 | - none
995 |
996 |
--------------------------------------------------------------------------------
/docs/todo.md:
--------------------------------------------------------------------------------
1 | ### TODO
2 | - GeoDistance
3 |
4 |
5 | ### 常用函数库
6 | https://bitbucket.org/justusmeyer/php-helper
7 | https://gitee.com/wuwenbin/php-helper
8 | https://github.com/HustGuoHeng/library
9 | https://github.com/Intervention/helper
10 | https://github.com/Osub/myFunction
11 | https://github.com/a67793581/php_function
12 | https://github.com/anerg2046/helper
13 | https://github.com/bearns/support
14 | https://github.com/bocharsky-bw/Arrayzy
15 | https://github.com/browner12/helpers
16 | https://github.com/cjango/helper
17 | https://github.com/codepso/php-helper
18 | https://github.com/coolswater/commonFunctions
19 | https://github.com/elinxer/library
20 | https://github.com/gyselroth/php-helper
21 | https://github.com/iamhefang/php-helpers
22 | https://github.com/ice-php/functions
23 | https://github.com/jasny/php-functions
24 | https://github.com/jstewmc/php-helpers
25 | https://github.com/koboshi/tool
26 | https://github.com/kohana/ohanzee-helpers
27 | https://github.com/liujin0506/jliu-helper
28 | https://github.com/lodash-php/lodash-php
29 | https://github.com/markrogoyski/math-php
30 | https://github.com/moxuandi/yii2-helpers
31 | https://github.com/mylukin/LazyFunc
32 | https://github.com/nkkollaw/zubr
33 | https://github.com/penglitao/library
34 | https://github.com/ppabcd/Helpers
35 | https://github.com/sethink/function-lib
36 | https://github.com/swoole/library
37 | https://github.com/tkhatibi/php-helper
38 | https://github.com/top-think/think-helper
39 | https://github.com/whm19940308/phpTools
40 | https://github.com/wycto/helper
41 | https://github.com/yunkaiyueming/php_lib_code_center
42 | https://github.com/zhosoft/tools
43 | https://github.com/voku/portable-utf8
44 | https://github.com/mtibben/html2text
45 | https://gitee.com/jingyiGit/php-utils
46 | https://gitee.com/ping_yuan/php-utils
47 | https://gitee.com/vipkwd/phputils
48 | https://gitee.com/myadder/utils-php
49 | https://gitee.com/aguage/php-utils
50 |
51 |
52 | promise
53 | https://github.com/hprose/hprose-php
54 | https://github.com/streamcommon/promise
55 | https://github.com/php-promise/promise
56 | https://docs.aws.amazon.com/zh_cn/sdk-for-php/v3/developer-guide/guide_promises.html
57 |
58 |
59 | ### 静态检查
60 | ```sh
61 | phpstan analyze src
62 | ```
63 |
--------------------------------------------------------------------------------
/src/Concurrent/CallableWrapper.php:
--------------------------------------------------------------------------------
1 | obj;
28 | return all(func_get_args())->then(function($args) use ($obj) {
29 | return co(call_user_func_array($obj, $args));
30 | });
31 | }
32 |
33 | }
--------------------------------------------------------------------------------
/src/Concurrent/Future.php:
--------------------------------------------------------------------------------
1 | resolve(call_user_func($computation));
88 | } catch (UncatchableException $e) {
89 | $previou = $e->getPrevious();
90 | throw (is_object($previou) ? $previou : $e);
91 | } catch (Throwable $e) {
92 | $this->reject($e);
93 | }
94 | }
95 | }
96 |
97 |
98 | /**
99 | * 私有调用
100 | * @param callable $callback
101 | * @param Future $next
102 | * @param mixed $params
103 | * @throws Throwable
104 | */
105 | private function privateCall(callable $callback, Future $next, $params) {
106 | try {
107 | $r = call_user_func($callback, $params);
108 | $next->resolve($r);
109 | } catch (UncatchableException $e) {
110 | $previou = $e->getPrevious();
111 | throw (is_object($previou) ? $previou : $e);
112 | } catch (Throwable $e) {
113 | $next->reject($e);
114 | }
115 | }
116 |
117 |
118 | /**
119 | * 私有解决
120 | * @param callable $onfulfill 成功事件
121 | * @param Future $next
122 | * @param mixed $params
123 | * @throws Throwable
124 | */
125 | private function privateResolve($onfulfill, Future $next, $params) {
126 | if (is_callable($onfulfill)) {
127 | $this->privateCall($onfulfill, $next, $params);
128 | } else {
129 | $next->resolve($params);
130 | }
131 | }
132 |
133 |
134 | /**
135 | * 私有拒绝
136 | * @param callable $onreject 失败事件
137 | * @param Future $next
138 | * @param mixed $params
139 | * @throws Throwable
140 | */
141 | private function privateReject($onreject, Future $next, $params) {
142 | if (is_callable($onreject)) {
143 | $this->privateCall($onreject, $next, $params);
144 | } else {
145 | $next->reject($params);
146 | }
147 | }
148 |
149 |
150 | /**
151 | * 解决
152 | * 该方法可以将状态为待定(pending)的 promise 对象变为成功(fulfilled)状态
153 | * @param mixed $value
154 | * @throws Throwable
155 | */
156 | public function resolve($value) {
157 | if ($value === $this) {
158 | $this->reject(new TypeError('Self resolution'));
159 | return;
160 | } elseif (isFuture($value)) {
161 | $value->fill($this);
162 | return;
163 | }
164 |
165 | $then = null;
166 | if (is_callable($value)) {
167 | $then = $value;
168 | } elseif (is_object($value) && method_exists($value, 'then')) {
169 | $then = [$value, 'then'];
170 | }elseif(is_string($value) && class_exists($value) && method_exists($value, 'then')){
171 | $obj = new $value();
172 | $then = [$obj, 'then'];
173 | }
174 |
175 | if(!is_null($then)) {
176 | $notrun = true;
177 | $self = $this;
178 | try {
179 | call_user_func($then, function ($y) use (&$notrun, $self) {
180 | if ($notrun) {
181 | $notrun = false;
182 | $self->resolve($y);
183 | }
184 | }, function ($r) use (&$notrun, $self) {
185 | if ($notrun) {
186 | $notrun = false;
187 | $self->reject($r);
188 | }
189 | });
190 | } catch (UncatchableException $e) {
191 | $previou = $e->getPrevious();
192 | throw (is_object($previou) ? $previou : $e);
193 | } catch (Throwable $e) {
194 | if ($notrun) {
195 | $notrun = false;
196 | $this->reject($e);
197 | }
198 | }
199 | return;
200 | }
201 |
202 | if ($this->state === self::PENDING) {
203 | $this->state = self::FULFILLED;
204 | $this->value = $value;
205 | while (count($this->subscribers) > 0) {
206 | $subscriber = array_shift($this->subscribers);
207 | $this->privateResolve($subscriber['onfulfill'], $subscriber['next'], $value);
208 | }
209 | }
210 | }
211 |
212 |
213 | /**
214 | * 拒绝
215 | * 该方法可以将状态为待定(pending)的 promise 对象变为失败(rejected)状态
216 | * @param $reason
217 | * @throws Throwable
218 | */
219 | public function reject($reason) {
220 | if ($this->state === self::PENDING) {
221 | $this->state = self::REJECTED;
222 | $this->reason = $reason;
223 | while (count($this->subscribers) > 0) {
224 | $subscriber = array_shift($this->subscribers);
225 | $this->privateReject($subscriber['onreject'], $subscriber['next'], $reason);
226 | }
227 | }
228 | }
229 |
230 |
231 | /**
232 | * 将要
233 | * 支持链式调用.
234 | * @param mixed $onfulfill 当成功时的执行体
235 | * @param mixed $onreject 当失败时的执行体
236 | * @return Future
237 | * @throws Throwable
238 | */
239 | public function then($onfulfill, $onreject = null): Future {
240 | if (!is_callable($onfulfill)) {
241 | $onfulfill = null;
242 | }
243 | if (!is_callable($onreject)) {
244 | $onreject = null;
245 | }
246 |
247 | $next = new Future();
248 |
249 | if ($this->state === self::FULFILLED) {
250 | $this->privateResolve($onfulfill, $next, $this->value);
251 | } elseif ($this->state === self::REJECTED) {
252 | $this->privateReject($onreject, $next, $this->reason);
253 | } else {
254 | array_push($this->subscribers, ['onfulfill' => $onfulfill, 'onreject' => $onreject, 'next' => $next]);
255 | }
256 |
257 | return $next;
258 | }
259 |
260 |
261 | /**
262 | * 完成
263 | * 类似then,但无返回值,不支持链式调用;用于单元测试.
264 | * @param $onfulfill
265 | * @param null $onreject
266 | * @throws Throwable
267 | */
268 | public function done($onfulfill, $onreject = null): void {
269 | $this->then($onfulfill, $onreject)->then(null, function (Throwable $error) {
270 | throw new UncatchableException("", 0, $error);
271 | });
272 | }
273 |
274 |
275 | /**
276 | * 失败
277 | * 该方法是 done(null, $onreject) 的简化.用于单元测试.
278 | * @param $onreject
279 | * @throws Throwable
280 | */
281 | public function fail($onreject): void {
282 | $this->done(null, $onreject);
283 | }
284 |
285 |
286 | /**
287 | * 当完成时(无论成功或失败).
288 | * @param callable $fn 执行体
289 | * @return Future
290 | * @throws Throwable
291 | */
292 | public function whenComplete(callable $fn): Future {
293 | return $this->then(function ($v) use ($fn) {
294 | makeClosureFun($fn, $v)();
295 | return $v;
296 | }, function ($e) use ($fn) {
297 | makeClosureFun($fn, $e)();
298 | throw $e;
299 | });
300 | }
301 |
302 |
303 | /**
304 | * 完成
305 | * 无论成功或失败,支持链式调用.
306 | * 是then(oncomplete, oncomplete)的简化
307 | * @param callable $oncomplete
308 | * @return Future
309 | * @throws Throwable
310 | */
311 | public function complete(callable $oncomplete = null): Future {
312 | $oncomplete = $oncomplete ?: function ($v) {
313 | return $v;
314 | };
315 | return $this->then($oncomplete, $oncomplete);
316 | }
317 |
318 |
319 | /**
320 | * 总是
321 | * 无论成功或失败,不支持链式.
322 | * 是done(oncomplete, oncomplete) 的简化
323 | * @param callable $oncomplete
324 | * @throws Throwable
325 | */
326 | public function always(callable $oncomplete): void {
327 | $this->done($oncomplete, $oncomplete);
328 | }
329 |
330 |
331 | /**
332 | * 将当前 promise 对象的值充填到参数所表示的 promise 对象中
333 | * @param $future
334 | * @throws Throwable
335 | */
336 | public function fill($future): void {
337 | $this->then([$future, 'resolve'], [$future, 'reject']);
338 | }
339 |
340 |
341 | /**
342 | * then成功后简写,将结果(单一值)作为回调参数.
343 | * @param callable $onfulfilledCallback
344 | * @return Future
345 | * @throws Throwable
346 | */
347 | public function tap(callable $onfulfilledCallback): Future {
348 | return $this->then(function ($result) use ($onfulfilledCallback) {
349 | call_user_func($onfulfilledCallback, $result);
350 | return $result;
351 | });
352 | }
353 |
354 |
355 | /**
356 | * then成功后简写,将结果(数组)作为回调函数的参数.
357 | * @param callable $onfulfilledCallback
358 | * @return Future
359 | * @throws Throwable
360 | */
361 | public function spread(callable $onfulfilledCallback): Future {
362 | return $this->then(function ($array) use ($onfulfilledCallback) {
363 | return call_user_func_array($onfulfilledCallback, $array);
364 | });
365 | }
366 |
367 |
368 | /**
369 | * 返回当前 promise 对象的状态
370 | * 如果当前状态为待定(pending),返回值为:['state' => 'pending']
371 | * 如果当前状态为成功(fulfilled),返回值为:['state' => 'fulfilled', 'value' => $promise->value]
372 | * 如果当前状态为失败(rejected),返回值为:['state' => 'rejected', 'reason' => $promise->reason]
373 | * @return array
374 | */
375 | public function inspect(): array {
376 | $res = ['state' => $this->state,];
377 |
378 | switch ($this->state) {
379 | case self::PENDING:
380 | break;
381 | case self::FULFILLED:
382 | $res['value'] = $this->value;
383 | break;
384 | case self::REJECTED:
385 | $res['reason'] = $this->reason;
386 | break;
387 | }
388 |
389 | return $res;
390 | }
391 |
392 |
393 | /**
394 | * 获取结果
395 | * @return mixed|null
396 | */
397 | public function getResult() {
398 | $status = $this->inspect();
399 | return $status['value'] ?? null;
400 | }
401 |
402 |
403 | /**
404 | * 获取原因
405 | * @return mixed|null
406 | */
407 | public function getReason() {
408 | $status = $this->inspect();
409 | return $status['reason'] ?? null;
410 | }
411 |
412 |
413 | /**
414 | * 是否正待定
415 | * @return bool
416 | */
417 | public function isPending(): bool {
418 | return $this->state === self::PENDING;
419 | }
420 |
421 |
422 | /**
423 | * 是否已成功
424 | * @return bool
425 | */
426 | public function isFulfilled(): bool {
427 | return $this->state === self::FULFILLED;
428 | }
429 |
430 |
431 | /**
432 | * 是否已失败
433 | * @return bool
434 | */
435 | public function isRejected(): bool {
436 | return $this->state === self::REJECTED;
437 | }
438 |
439 |
440 | /**
441 | * 是否已完成
442 | * @return bool
443 | */
444 | public function isCompleted(): bool {
445 | return in_array($this->state, [self::FULFILLED, self::REJECTED]);
446 | }
447 |
448 |
449 | /**
450 | * 捕获错误
451 | * 该方法是 then(null, $onreject) 的简化.
452 | * @param $onreject
453 | * @param callable $fn
454 | * @return Future
455 | * @throws Throwable
456 | */
457 | public function catchError($onreject, callable $fn = null): Future {
458 | if (is_callable($fn)) {
459 | $self = $this;
460 | return $this->then(null, function ($e) use ($self, $onreject, $fn) {
461 | if (call_user_func($fn, $e)) {
462 | return $self->then(null, $onreject);
463 | } elseif($e instanceof Throwable) {
464 | throw $e;
465 | }else{
466 | throw new BaseException(strval($e));
467 | }
468 | });
469 | }
470 | return $this->then(null, $onreject);
471 | }
472 |
473 |
474 | /**
475 | * 获取状态或结果key的值
476 | * @param string $key
477 | * @return string|Future
478 | * @throws Throwable
479 | */
480 | public function __get(string $key) {
481 | if ($key == 'state') {
482 | return $this->state;
483 | }
484 | return $this->then(function ($result) use ($key) {
485 | $res = null;
486 | if (is_object($result)) {
487 | $res = $result->$key ?? null;
488 | }elseif (is_array($result)) {
489 | $res = $result[$key] ?? null;
490 | }
491 |
492 | return $res;
493 | });
494 | }
495 |
496 |
497 | /**
498 | * 自动调用方法
499 | * @param string $method 方法名
500 | * @param array $args 参数
501 | * @return Future
502 | * @throws Throwable
503 | */
504 | public function __call(string $method, array $args): Future {
505 | return $this->then(function ($result) use ($method, $args) {
506 | return all($args)->then(function ($args) use ($result, $method) {
507 | return call_user_func_array([$result, $method], $args);
508 | });
509 | });
510 | }
511 |
512 |
513 | }
--------------------------------------------------------------------------------
/src/Concurrent/Promise.php:
--------------------------------------------------------------------------------
1 | resolve($value);
35 | }, function ($reason = null) use ($self) {
36 | $self->reject($reason);
37 | });
38 | }
39 | }
40 |
41 | }
--------------------------------------------------------------------------------
/src/Concurrent/Wrapper.php:
--------------------------------------------------------------------------------
1 | obj = $obj;
28 | }
29 |
30 |
31 | /**
32 | * @param $name
33 | * @param array $arguments
34 | * @return Future
35 | * @throws Throwable
36 | */
37 | public function __call($name, array $arguments):Future {
38 | $method = [$this->obj, $name];
39 | return all($arguments)->then(function($args) use ($method, $name) {
40 | return co(call_user_func_array($method, $args));
41 | });
42 | }
43 |
44 |
45 | public function __get($name) {
46 | return $this->obj->$name ?? null;
47 | }
48 |
49 |
50 | public function __set($name, $value) {
51 | $this->obj->$name = $value;
52 | }
53 |
54 |
55 | public function __isset($name) {
56 | return isset($this->obj->$name);
57 | }
58 |
59 |
60 | public function __unset($name) {
61 | unset($this->obj->$name);
62 | }
63 |
64 |
65 | }
--------------------------------------------------------------------------------
/src/Consts.php:
--------------------------------------------------------------------------------
1 | getMessage() . ' ##code:' . $this->getCode() . ' ##file:' . $this->getFile() . ' ##line:' . $this->getLine();
29 | return $msg;
30 | }
31 |
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/src/Exceptions/UncatchableException.php:
--------------------------------------------------------------------------------
1 | $item) {
32 | if (is_array($item) && !empty($item)) {
33 | $arr[$k] = array_map(__METHOD__, $item);
34 | } elseif (is_object($item)) {
35 | $arr[$k] = self::object2Array($item);
36 | }
37 | }
38 | } else {
39 | $arr = (array)$arr;
40 | }
41 |
42 | return $arr;
43 | }
44 |
45 |
46 | /**
47 | * 数组转对象
48 | * @param array $arr
49 | * @return object
50 | */
51 | public static function array2Object(array $arr): object {
52 | foreach ($arr as $k => $item) {
53 | if (is_array($item)) {
54 | $arr[$k] = empty($item) ? new \stdClass() : call_user_func(__METHOD__, $item);
55 | }
56 | }
57 |
58 | return (object)$arr;
59 | }
60 |
61 |
62 | /**
63 | * 字符串转十六进制
64 | * @param string $str
65 | * @return string
66 | */
67 | public static function str2hex(string $str): string {
68 | $res = '';
69 | for ($i = 0; $i < strlen($str); $i++) {
70 | $val = dechex(ord($str[$i]));
71 | if (strlen($val) < 2) {
72 | $val = "0" . $val;
73 | }
74 | $res .= $val;
75 | }
76 |
77 | return $res;
78 | }
79 |
80 |
81 | /**
82 | * 十六进制转字符串
83 | * @param string $str
84 | * @return string
85 | */
86 | public static function hex2Str(string $str): string {
87 | $res = '';
88 | $str = strtolower($str);
89 | for ($i = 0; $i < strlen($str); $i += 2) {
90 | $item = substr($str, $i, 2);
91 | $item = hexdec($item);
92 | $val = chr($item);
93 | $res .= $val;
94 | }
95 |
96 | return $res;
97 | }
98 |
99 |
100 | }
--------------------------------------------------------------------------------
/src/Helpers/DebugHelper.php:
--------------------------------------------------------------------------------
1 | '致命运行时错误(E_ERROR)',
21 | '2' => '运行时警告(E_WARNING)',
22 | '4' => '编译时语法解析错误(E_PARSE)',
23 | '8' => '运行时提示(E_NOTICE)',
24 | '16' => 'PHP初始化致命错误(E_CORE_ERROR)',
25 | '32' => 'PHP初始化警告(E_CORE_WARNING)',
26 | '64' => 'Zend致命编译时错误(E_COMPILE_ERROR)',
27 | '128' => 'Zend编译时警告(E_COMPILE_WARNING)',
28 | '256' => '用户产生的错误(E_USER_ERROR)',
29 | '512' => '用户产生的警告(E_USER_WARNING)',
30 | '1024' => '用户产生的提示(E_USER_NOTICE)',
31 | '2048' => '代码提示(E_STRICT)',
32 | '4096' => '可捕获的致命错误(E_RECOVERABLE_ERROR)',
33 | '8192' => '运行时提示(E_DEPRECATED)',
34 | '16384' => '用户警告信息(E_USER_DEPRECATED)',
35 | '30719' => '所有错误警告(E_ALL)',
36 | ];
37 |
38 |
39 | /**
40 | * 错误日志捕获(只适用于php7)
41 | * @param string $logFile
42 | */
43 | public static function errorLogHandler(string $logFile = ''): void {
44 | if (empty($logFile)) {
45 | $tmpDir = sys_get_temp_dir();
46 | $logFile = $tmpDir . '/phperr_' . date('Ymd') . '.log';
47 | } elseif (!file_exists($logFile)) {
48 | @touch($logFile);
49 | }
50 |
51 | ini_set('log_errors', 1); //设置错误信息输出到文件
52 | ini_set('ignore_repeated_errors', 1);//不重复记录出现在同一个文件中的同一行代码上的错误信息
53 |
54 | $error = error_get_last();//获取最后发生的错误
55 | if (is_array($error)) {
56 | $errorType = self::ERROR_TYPES[$error['type']] ?? '未知类型';
57 |
58 | $msg = sprintf('[%s] %s %s %s line:%s',
59 | date("Y-m-d H:i:s"),
60 | $errorType,
61 | $error['message'],
62 | $error['file'],
63 | $error['line']);
64 |
65 | //必须显式地记录错误
66 | error_log($msg . "\r\n", 3, $logFile);
67 | }
68 | }
69 |
70 |
71 | }
--------------------------------------------------------------------------------
/src/Helpers/DirectoryHelper.php:
--------------------------------------------------------------------------------
1 | $file) {
62 | $fpath = $file->getRealPath();
63 | if ($file->isDir()) {
64 | if ($type != 'file') {
65 | array_push($tree, $fpath);
66 | }
67 | } elseif ($type != 'dir') {
68 | array_push($tree, $fpath);
69 | }
70 | }
71 | } elseif (is_file($path) && $type != 'dir') {
72 | array_push($tree, $path);
73 | }
74 |
75 | return $tree;
76 | }
77 |
78 |
79 | /**
80 | * 获取目录大小,单位[字节]
81 | * @param string $path
82 | * @return int
83 | */
84 | public static function getDirSize(string $path): int {
85 | $size = 0;
86 | if ($path == '' || !is_dir($path)) {
87 | return $size;
88 | }
89 |
90 | $dh = @opendir($path); //比dir($path)快
91 | while (false != ($file = @readdir($dh))) {
92 | if ($file != '.' and $file != '..') {
93 | $fielpath = $path . DIRECTORY_SEPARATOR . $file;
94 | if (is_dir($fielpath)) {
95 | $size += self::getDirSize($fielpath);
96 | } else {
97 | $size += filesize($fielpath);
98 | }
99 | }
100 | }
101 | @closedir($dh);
102 | return $size;
103 | }
104 |
105 |
106 | /**
107 | * 拷贝目录
108 | * @param string $from 源目录
109 | * @param string $dest 目标目录
110 | * @param bool $cover 是否覆盖已存在的文件
111 | * @return bool
112 | */
113 | public static function copyDir(string $from, string $dest, bool $cover = false): bool {
114 | if (!file_exists($dest) && !@mkdir($dest, 0766, true)) {
115 | return false;
116 | }
117 |
118 | $dh = @opendir($from);
119 | while (false !== ($fileName = @readdir($dh))) {
120 | if (($fileName != ".") && ($fileName != "..")) {
121 | $newFile = "$dest/$fileName";
122 | if (!is_dir("$from/$fileName")) {
123 | if (file_exists($newFile) && !$cover) {
124 | continue;
125 | } elseif (!copy("$from/$fileName", $newFile)) {
126 | return false;
127 | }
128 | } else {
129 | self::copyDir("$from/$fileName", $newFile, $cover);
130 | }
131 | }
132 | }
133 | @closedir($dh);
134 |
135 | return true;
136 | }
137 |
138 |
139 | /**
140 | * 批量改变目录模式(包括子目录和所属文件)
141 | * @param string $path 路径
142 | * @param int $filemode 文件模式
143 | * @param int $dirmode 目录模式
144 | */
145 | public static function chmodBatch(string $path, int $filemode = 0766, int $dirmode = 0766): void {
146 | if ($path == '') {
147 | return;
148 | }
149 |
150 | if (is_dir($path)) {
151 | if (!@chmod($path, $dirmode)) {
152 | return;
153 | }
154 | $dh = @opendir($path);
155 | while (($file = @readdir($dh)) !== false) {
156 | if ($file != '.' && $file != '..') {
157 | $fullpath = $path . '/' . $file;
158 | self::chmodBatch($fullpath, $filemode, $dirmode);
159 | }
160 | }
161 | @closedir($dh);
162 | } elseif (!is_link($path)) {
163 | @chmod($path, $filemode);
164 | }
165 | }
166 |
167 |
168 | /**
169 | * 删除目录(目录下所有文件,包括本目录)
170 | * @param string $path
171 | * @return bool
172 | */
173 | public static function delDir(string $path): bool {
174 | if (is_dir($path) && $dh = @opendir($path)) {
175 | while (false != ($file = @readdir($dh))) {
176 | if ($file != '.' && $file != '..') {
177 | $fielpath = $path . DIRECTORY_SEPARATOR . $file;
178 | if (is_dir($fielpath)) {
179 | self::delDir($fielpath);
180 | } else {
181 | @unlink($fielpath);
182 | }
183 | }
184 | }
185 | @closedir($dh);
186 | return @rmdir($path);
187 | }
188 | return false;
189 | }
190 |
191 |
192 | /**
193 | * 清空目录(删除目录下所有文件,仅保留当前目录)
194 | * @param string $path
195 | * @return bool
196 | */
197 | public static function clearDir(string $path): bool {
198 | if ($path == '' || !is_dir($path)) {
199 | return false;
200 | }
201 |
202 | $dirs = [];
203 | $dir = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
204 | $iterator = new RecursiveIteratorIterator($dir, RecursiveIteratorIterator::CHILD_FIRST);
205 |
206 | foreach ($iterator as $single => $file) {
207 | $fpath = $file->getRealPath();
208 | if ($file->isDir()) {
209 | array_push($dirs, $fpath);
210 | } else {
211 | //先删除文件
212 | @unlink($fpath);
213 | }
214 | }
215 |
216 | //再删除目录
217 | rsort($dirs);
218 | foreach ($dirs as $dir) {
219 | @rmdir($dir);
220 | }
221 |
222 | unset($dir, $iterator, $dirs);
223 | return true;
224 | }
225 |
226 |
227 | /**
228 | * 格式化路径字符串(路径后面加/)
229 | * @param string $dir
230 | * @return string
231 | */
232 | public static function formatDir(string $dir): string {
233 | if ($dir == '') {
234 | return '';
235 | }
236 |
237 | $old = [
238 | '\\',
239 | '|',
240 | '<',
241 | '>',
242 | '?',
243 | ];
244 | $replace = [
245 | '/',
246 | '',
247 | '',
248 | '',
249 | '',
250 | ];
251 |
252 | $dir = str_replace($old, $replace, $dir);
253 | $dir = preg_replace(RegularHelper::$patternDoubleSlash, '/', $dir);
254 |
255 | //是否有":"
256 | if (StringHelper::contains($dir, ':')) {
257 | $arr = explode('/', $dir);
258 | $first = array_shift($arr);
259 |
260 | //win下的路径,如C:\Users\Administrator
261 | if (!ValidateHelper::endsWith($first, ':')) {
262 | $first = str_replace(':', '', $first);
263 | }
264 |
265 | $last = implode('/', $arr);
266 | $last = str_replace(':', '', $last);
267 | $dir = "{$first}/{$last}";
268 | }
269 |
270 | return rtrim($dir, ' / ') . '/';
271 | }
272 |
273 |
274 | }
--------------------------------------------------------------------------------
/src/Helpers/EncryptHelper.php:
--------------------------------------------------------------------------------
1 | 0) {
123 | return [substr($res, 26), $expTime];
124 | }
125 |
126 | return ['', $expTime];
127 | }
128 | }
129 |
130 |
131 | /**
132 | * 简单加密
133 | * @param string $data 数据
134 | * @param string $key 密钥
135 | * @return string
136 | */
137 | public static function easyEncrypt(string $data, string $key): string {
138 | if ($data == '') {
139 | return '';
140 | }
141 |
142 | $key = md5($key);
143 | $dataLen = strlen($data);
144 | $keyLen = strlen($key);
145 | $x = 0;
146 | $str = $char = '';
147 | for ($i = 0; $i < $dataLen; $i++) {
148 | if ($x == $keyLen) {
149 | $x = 0;
150 | }
151 |
152 | $str .= chr(ord($data[$i]) + (ord($key[$x])) % 256);
153 | $x++;
154 | }
155 |
156 | return substr($key, 0, Consts::DYNAMIC_KEY_LEN) . self::base64UrlEncode($str);
157 | }
158 |
159 |
160 | /**
161 | * 简单解密
162 | * @param string $data 数据
163 | * @param string $key 密钥
164 | * @return string
165 | */
166 | public static function easyDecrypt(string $data, string $key): string {
167 | if (strlen($data) < Consts::DYNAMIC_KEY_LEN) {
168 | return '';
169 | }
170 |
171 | $key = md5($key);
172 | if (substr($key, 0, Consts::DYNAMIC_KEY_LEN) != substr($data, 0, Consts::DYNAMIC_KEY_LEN)) {
173 | return '';
174 | }
175 |
176 | $data = self::base64UrlDecode(substr($data, Consts::DYNAMIC_KEY_LEN));
177 | if (empty($data)) {
178 | return '';
179 | }
180 |
181 | $dataLen = strlen($data);
182 | $keyLen = strlen($key);
183 | $x = 0;
184 | $str = $char = '';
185 | for ($i = 0; $i < $dataLen; $i++) {
186 | if ($x == $keyLen) {
187 | $x = 0;
188 | }
189 |
190 | $c = ord($data[$i]);
191 | $k = ord($key[$x]);
192 | if ($c < $k) {
193 | $str .= chr(($c + 256) - $k);
194 | } else {
195 | $str .= chr($c - $k);
196 | }
197 |
198 | $x++;
199 | }
200 |
201 | return $str;
202 | }
203 |
204 |
205 | /**
206 | * MurmurHash3算法函数
207 | * @param string $data 要哈希的数据
208 | * @param int $seed 随机种子(仅素数)
209 | * @param bool $unsign 是否返回无符号值;为true时返回11位无符号整数,为false时返回10位有符号整数
210 | * @return float|int
211 | */
212 | public static function murmurhash3Int(string $data, int $seed = 3, bool $unsign = true) {
213 | $key = array_values(unpack('C*', $data));
214 | $klen = count($key);
215 | $h1 = abs($seed);
216 | for ($i = 0, $bytes = $klen - ($remainder = $klen & 3); $i < $bytes;) {
217 | $k1 = $key[$i] | ($key[++$i] << 8) | ($key[++$i] << 16) | ($key[++$i] << 24);
218 | ++$i;
219 | $k1 = (((($k1 & 0xffff) * 0xcc9e2d51) + ((((($k1 >= 0 ? $k1 >> 16 : (($k1 & 0x7fffffff) >> 16) | 0x8000)) * 0xcc9e2d51) & 0xffff) << 16))) & 0xffffffff;
220 | $k1 = $k1 << 15 | ($k1 >= 0 ? $k1 >> 17 : (($k1 & 0x7fffffff) >> 17) | 0x4000);
221 | $k1 = (((($k1 & 0xffff) * 0x1b873593) + ((((($k1 >= 0 ? $k1 >> 16 : (($k1 & 0x7fffffff) >> 16) | 0x8000)) * 0x1b873593) & 0xffff) << 16))) & 0xffffffff;
222 | $h1 ^= $k1;
223 | $h1 = $h1 << 13 | ($h1 >= 0 ? $h1 >> 19 : (($h1 & 0x7fffffff) >> 19) | 0x1000);
224 | $h1b = (((($h1 & 0xffff) * 5) + ((((($h1 >= 0 ? $h1 >> 16 : (($h1 & 0x7fffffff) >> 16) | 0x8000)) * 5) & 0xffff) << 16))) & 0xffffffff;
225 | $h1 = ((($h1b & 0xffff) + 0x6b64) + ((((($h1b >= 0 ? $h1b >> 16 : (($h1b & 0x7fffffff) >> 16) | 0x8000)) + 0xe654) & 0xffff) << 16));
226 | }
227 | $k1 = 0;
228 | switch ($remainder) {
229 | case 3:
230 | $k1 ^= $key[$i + 2] << 16;
231 | case 2:
232 | $k1 ^= $key[$i + 1] << 8;
233 | case 1:
234 | $k1 ^= $key[$i];
235 | $k1 = ((($k1 & 0xffff) * 0xcc9e2d51) + ((((($k1 >= 0 ? $k1 >> 16 : (($k1 & 0x7fffffff) >> 16) | 0x8000)) * 0xcc9e2d51) & 0xffff) << 16)) & 0xffffffff;
236 | $k1 = $k1 << 15 | ($k1 >= 0 ? $k1 >> 17 : (($k1 & 0x7fffffff) >> 17) | 0x4000);
237 | $k1 = ((($k1 & 0xffff) * 0x1b873593) + ((((($k1 >= 0 ? $k1 >> 16 : (($k1 & 0x7fffffff) >> 16) | 0x8000)) * 0x1b873593) & 0xffff) << 16)) & 0xffffffff;
238 | $h1 ^= $k1;
239 | }
240 | $h1 ^= $klen;
241 | $h1 ^= ($h1 >= 0 ? $h1 >> 16 : (($h1 & 0x7fffffff) >> 16) | 0x8000);
242 | $h1 = ((($h1 & 0xffff) * 0x85ebca6b) + ((((($h1 >= 0 ? $h1 >> 16 : (($h1 & 0x7fffffff) >> 16) | 0x8000)) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff;
243 | $h1 ^= ($h1 >= 0 ? $h1 >> 13 : (($h1 & 0x7fffffff) >> 13) | 0x40000);
244 | $h1 = (((($h1 & 0xffff) * 0xc2b2ae35) + ((((($h1 >= 0 ? $h1 >> 16 : (($h1 & 0x7fffffff) >> 16) | 0x8000)) * 0xc2b2ae35) & 0xffff) << 16))) & 0xffffffff;
245 | $h1 ^= ($h1 >= 0 ? $h1 >> 16 : (($h1 & 0x7fffffff) >> 16) | 0x8000);
246 |
247 | if ($unsign) {
248 | $h1 = ($h1 >= 0) ? bcadd('1' . str_repeat('0', 10), $h1) : abs($h1);
249 | }
250 |
251 | return $h1;
252 | }
253 |
254 |
255 | /**
256 | * openssl加密
257 | * @param string $str 明文
258 | * @param string $key 密钥
259 | * @param string $iv 初始化向量
260 | * @param string $method 密码学方式
261 | * @return string
262 | * @throws Exception
263 | */
264 | public static function opensslEncrypt(string $str, string $key, string $iv = '', string $method = 'AES-128-CBC'): string {
265 | if (!extension_loaded('openssl')) {
266 | throw new Exception("You need to install OpenSSL module.");
267 | }
268 |
269 | if ($str == '') {
270 | return '';
271 | }
272 | if ($key == '') {
273 | $key = md5($key);
274 | }
275 |
276 | $hx = hash('sha256', $iv);
277 | $ivLen = openssl_cipher_iv_length($method);
278 | $ivStr = substr($hx, 0, $ivLen);
279 | $res = openssl_encrypt($str, $method, $key, 0, $ivStr);
280 |
281 | return $res ? base64_encode($res) : '';
282 | }
283 |
284 |
285 | /**
286 | * openssl解密
287 | * @param string $str 密文
288 | * @param string $key 密钥
289 | * @param string $iv 初始化向量
290 | * @param string $method 密码学方式
291 | * @return string
292 | * @throws Exception
293 | */
294 | public static function opensslDecrypt(string $str, string $key, string $iv = '', string $method = 'AES-128-CBC'): string {
295 | if (!extension_loaded('openssl')) {
296 | throw new Exception("You need to install OpenSSL module.");
297 | }
298 |
299 | if ($str == '') {
300 | return '';
301 | }
302 | if ($key == '') {
303 | $key = md5($key);
304 | }
305 |
306 | $str = base64_decode($str);
307 | $hx = hash('sha256', $iv);
308 | $ivLen = openssl_cipher_iv_length($method);
309 | $ivStr = substr($hx, 0, $ivLen);
310 | $res = openssl_decrypt($str, $method, $key, 0, $ivStr);
311 |
312 | return $res ? $res : '';
313 | }
314 |
315 |
316 | }
--------------------------------------------------------------------------------
/src/Helpers/NumberHelper.php:
--------------------------------------------------------------------------------
1 | = 1024 && $i < 5; $i++) {
32 | $size /= 1024;
33 | }
34 |
35 | return round($size, $dec) . $delimiter . ($units[$i] ?? Consts::UNKNOWN);
36 | }
37 |
38 |
39 | /**
40 | * 值是否在某范围内
41 | * @param int|float $val 值
42 | * @param int|float $min 小值
43 | * @param int|float $max 大值
44 | * @return bool
45 | */
46 | public static function inRange($val, $min, $max): bool {
47 | $val = floatval($val);
48 | $min = floatval($min);
49 | $max = floatval($max);
50 | return $val >= $min && $val <= $max;
51 | }
52 |
53 |
54 | /**
55 | * 对数列求和,忽略非数值.
56 | * @param mixed ...$vals
57 | * @return float
58 | */
59 | public static function sum(...$vals): float {
60 | $res = 0;
61 | foreach ($vals as $val) {
62 | if (is_numeric($val)) {
63 | $res += floatval($val);
64 | }
65 | }
66 |
67 | return $res;
68 | }
69 |
70 |
71 | /**
72 | * 对数列求平均值,忽略非数值.
73 | * @param mixed ...$vals
74 | * @return float
75 | */
76 | public static function average(...$vals): float {
77 | $res = 0;
78 | $count = 0;
79 | $total = 0;
80 | foreach ($vals as $val) {
81 | if (is_numeric($val)) {
82 | $total += floatval($val);
83 | $count++;
84 | }
85 | }
86 |
87 | if ($count > 0) {
88 | $res = $total / $count;
89 | }
90 |
91 | return $res;
92 | }
93 |
94 |
95 | /**
96 | * 获取地理距离/米.
97 | * 参数分别为两点的经度和纬度.lat:-90~90,lng:-180~180.
98 | * @param float $lng1 起点经度
99 | * @param float $lat1 起点纬度
100 | * @param float $lng2 终点经度
101 | * @param float $lat2 终点纬度
102 | * @return float
103 | */
104 | public static function geoDistance(float $lng1 = 0, float $lat1 = 0, float $lng2 = 0, float $lat2 = 0): float {
105 | $earthRadius = 6371000.0;
106 | $lat1 = ($lat1 * pi()) / 180;
107 | $lng1 = ($lng1 * pi()) / 180;
108 | $lat2 = ($lat2 * pi()) / 180;
109 | $lng2 = ($lng2 * pi()) / 180;
110 |
111 | $calcLongitude = $lng2 - $lng1;
112 | $calcLatitude = $lat2 - $lat1;
113 | $stepOne = pow(sin($calcLatitude / 2), 2) + cos($lat1) * cos($lat2) * pow(sin($calcLongitude / 2), 2);
114 | $stepTwo = 2 * asin(min(1, sqrt($stepOne)));
115 | $res = $earthRadius * $stepTwo;
116 |
117 | return $res;
118 | }
119 |
120 |
121 | /**
122 | * 数值格式化(会四舍五入)
123 | * @param float|int|string $number 要格式化的数字
124 | * @param int $decimals 小数位数
125 | * @return string
126 | */
127 | public static function numberFormat($number, int $decimals = 2): string {
128 | return number_format(floatval($number), $decimals, '.', '');
129 | }
130 |
131 |
132 | /**
133 | * 数值截取(不会四舍五入)
134 | * @param float|int|string $number 要格式化的数字
135 | * @param int $decimals 小数位数
136 | * @return float
137 | */
138 | public static function numberSub($number, int $decimals = 2): float {
139 | if ($decimals == 0 && ValidateHelper::isInteger($number)) {
140 | return floatval($number);
141 | }
142 |
143 | return intval(floatval($number) * pow(10, $decimals)) / pow(10, $decimals);
144 | }
145 |
146 |
147 | /**
148 | * 生成随机浮点数
149 | * @param float $min 小值
150 | * @param float $max 大值
151 | * @return float
152 | */
153 | public static function randFloat(float $min = 0, float $max = 1): float {
154 | return $min + mt_rand() / mt_getrandmax() * ($max - $min);
155 | }
156 |
157 |
158 | /**
159 | * 将金额转为大写人民币
160 | * @param float $num 金额,元(最大支持千亿)
161 | * @param int $decimals 精确小数位数(最大支持为3,即厘)
162 | * @return string
163 | * @throws BaseException
164 | */
165 | public static function money2Yuan(float $num, int $decimals = 0): string {
166 | $int = intval($num);
167 | if (strlen($int) > 12) {
168 | throw new BaseException('The maximum value supports 12 bits!');
169 | }
170 |
171 | $uppers = '零壹贰叁肆伍陆柒捌玖';
172 | $units = '元拾佰仟万拾佰仟亿拾佰仟';
173 |
174 | if ($decimals > 0) {
175 | $decimals = min($decimals, 3);
176 | $adds = ['角', '分', '厘'];
177 | $num = $num * pow(10, $decimals);
178 |
179 | for ($i = 0; $i < $decimals; $i++) {
180 | $units = $adds[$i] . $units;
181 | }
182 | }
183 |
184 | $res = '';
185 | $i = 0;
186 | while (true) {
187 | if ($i == 0) {
188 | $n = substr($num, strlen($num) - 1, 1);
189 | } else {
190 | $n = $num % 10;
191 | }
192 | $p1 = substr($uppers, 3 * $n, 3);
193 | $p2 = substr($units, 3 * $i, 3);
194 |
195 | if ($n != '0' || ($n == '0' && ($p2 == '亿' || $p2 == '万' || $p2 == '元'))) {
196 | $res = $p1 . $p2 . $res;
197 | } else {
198 | $res = $p1 . $res;
199 | }
200 |
201 | $i = $i + 1;
202 | $num = $num / 10;
203 | $num = (int)$num;
204 | if ($num == 0) {
205 | break;
206 | }
207 | }
208 |
209 | $j = 0;
210 | $len = strlen($res);
211 | while ($j < $len) {
212 | $m = substr($res, $j, 6);
213 | if ($m == '零元' || $m == '零万' || $m == '零亿' || $m == '零零') {
214 | $left = substr($res, 0, $j);
215 | $right = substr($res, $j + 3);
216 | $res = $left . $right;
217 | $j = $j - 3;
218 | $len = $len - 3;
219 | }
220 | $j = $j + 3;
221 | }
222 |
223 | if (substr($res, strlen($res) - 3, 3) == '零') {
224 | $res = substr($res, 0, strlen($res) - 3);
225 | }
226 |
227 | if (empty($res)) {
228 | return "零元整";
229 | } else {
230 | $res .= "整";
231 | }
232 |
233 | return $res;
234 | }
235 |
236 |
237 | /**
238 | * 求以 $base 为底 $num 的对数临近值
239 | * @param mixed $num 非负数
240 | * @param int $base 底数
241 | * @param bool $left 是否向左取整
242 | * @return int
243 | * @throws BaseException
244 | */
245 | public static function nearLogarithm($num, int $base = 2, bool $left = true): int {
246 | if (!is_numeric($num) || $num < 0) {
247 | throw new BaseException('The $num must be non-negative!');
248 | } elseif ($base <= 0) {
249 | throw new BaseException('The $base must be a positive integer!');
250 | }
251 |
252 | $log = log($num, $base);
253 |
254 | return $left ? intval($log) : intval(ceil($log));
255 | }
256 |
257 |
258 | /**
259 | * 将自然数按底数进行拆解
260 | * @param int $num 自然数
261 | * @param int $base 底数
262 | * @return array
263 | * @throws BaseException
264 | */
265 | public static function splitNaturalNum(int $num, int $base): array {
266 | if (!ValidateHelper::isNaturalNum($num)) {
267 | throw new BaseException('The $num must be a natural number!');
268 | } elseif ($base <= 0) {
269 | throw new BaseException('The $base must be a positive integer!');
270 | }
271 |
272 | $res = [];
273 | while ($num > $base) {
274 | $n = self::nearLogarithm($num, $base, true);
275 | $child = pow($base, $n);
276 | $num -= $child;
277 | array_push($res, $child);
278 | }
279 |
280 | if ($num > 0 || ($num == 0 && empty($res))) {
281 | array_push($res, $num);
282 | }
283 |
284 | return $res;
285 | }
286 |
287 |
288 | }
--------------------------------------------------------------------------------
/src/Helpers/RegularHelper.php:
--------------------------------------------------------------------------------
1 | '/\(([^\(]*?)\)/is',
277 | '2' => '/\[([^\[]*?)\]/is',
278 | '4' => '/\{([^\{]*?)\}/is',
279 | '8' => '/\<([^\<]*?)\>/is',
280 | '16' => '/(([^(]*?))/u',
281 | '32' => '/【([^【]*?)】/u',
282 | '64' => '/《([^《]*?)》/u',
283 | ];
284 |
285 |
286 | }
--------------------------------------------------------------------------------
/src/Helpers/UrlHelper.php:
--------------------------------------------------------------------------------
1 | $val) {
31 | $url = str_replace($val, urlencode($val), $url); //将转译替换中文
32 | }
33 | if (strpos($url, ' ')) {//若存在空格
34 | $url = str_replace(' ', '%20', $url);
35 | }
36 | }
37 | return $url;
38 | }
39 |
40 |
41 | /**
42 | * 中文urldecode
43 | * @param string $url
44 | * @return string
45 | */
46 | public static function cnUrldecode(string $url): string {
47 | $res = "";
48 | $pos = 0;
49 | $len = strlen($url);
50 | while ($pos < $len) {
51 | $charAt = substr($url, $pos, 1);
52 | if ($charAt == '%') {
53 | $pos++;
54 | $charAt = substr($url, $pos, 1);
55 | if ($charAt == 'u') {
56 | // we got a unicode character
57 | $pos++;
58 | $unicodeHexVal = substr($url, $pos, 4);
59 | $unicode = hexdec($unicodeHexVal);
60 | $entity = "" . $unicode . ';';
61 | $res .= utf8_encode($entity);
62 | $pos += 4;
63 | } else {
64 | // we have an escaped ascii character
65 | $hexVal = substr($url, $pos, 2);
66 | $res .= chr(hexdec($hexVal));
67 | $pos += 2;
68 | }
69 | } else {
70 | $res .= $charAt;
71 | $pos++;
72 | }
73 | }
74 | return $res;
75 | }
76 |
77 |
78 | /**
79 | * 根据键值对数组,组建uri(带?的url参数串).
80 | * 若$replaceKeys非空,而$replaceVals为空时,则是删除$replaceKeys包含的键(参数名).
81 | * @param array $params 参数数组,最多二维
82 | * @param array $replaceKeys 要替换的键
83 | * @param array $replaceVals 要替换的值
84 | * @return string
85 | */
86 | public static function buildUriParams(array $params, array $replaceKeys = [], array $replaceVals = []) {
87 | foreach ($replaceKeys as $key) {
88 | unset($params[$key]);
89 | }
90 |
91 | $res = '';
92 | foreach ($params as $k => $v) {
93 | if (is_array($v)) {
94 | foreach ($v as $k2 => $v2) {
95 | if (is_int($k2)) {
96 | $k2 = '';
97 | }
98 | $res .= "&{$k}[{$k2}]={$v2}";
99 | }
100 | } else {
101 | $res .= "&{$k}={$v}";
102 | }
103 | }
104 |
105 | if (!empty($replaceVals)) {
106 | foreach ($replaceVals as $k => $val) {
107 | $key = $replaceKeys[$k] ?? '';
108 | if (!empty($key)) {
109 | $res .= "&{$key}={$val}";
110 | }
111 | }
112 | }
113 |
114 | $res[0] = '?';
115 | return $res;
116 | }
117 |
118 |
119 | /**
120 | * 格式化URL(替换重复的//)
121 | * @param string $url
122 | * @return string
123 | */
124 | public static function formatUrl(string $url): string {
125 | if (!stripos($url, '://')) {
126 | $url = 'http://' . $url;
127 | }
128 | $url = str_replace("\\", "/", $url);
129 | return preg_replace('/([^:])[\/]{2,}/', '$1/', $url);
130 | }
131 |
132 |
133 | /**
134 | * 检查URL是否正常存在
135 | * @param string $url
136 | * @return bool
137 | */
138 | public static function checkUrlExists(string $url): bool {
139 | if (empty($url)) {
140 | return false;
141 | }
142 |
143 | if (!stripos($url, '://')) {
144 | $url = 'http://' . $url;
145 | }
146 | if (!ValidateHelper::isUrl($url)) {
147 | return false;
148 | }
149 |
150 | $header = @get_headers($url, true);
151 |
152 | return isset($header[0]) && (strpos($header[0], '200') || strpos($header[0], '304'));
153 | }
154 |
155 |
156 | /**
157 | * 将URL转换为链接标签
158 | * @param string $url 含URL的字符串
159 | * @param array $protocols 要转换的协议, http/https, ftp/ftps, mail
160 | * @param string $target 是否新页面打开:_blank,_self,默认为空
161 | * @return string
162 | */
163 | public static function url2Link(string $url, array $protocols = ['http', 'https'], string $target = ''): string {
164 | if (!empty($url)) {
165 | if (!empty(array_intersect($protocols, ['http', 'https']))) {
166 | $pattern = '@(http(s)?)?(://)?(([a-zA-Z])([-\w]+\.)+([^\s\.]+[^\s]*)+[^,.\s])@i';
167 | $url = preg_replace($pattern, "$0", $url);
168 | }
169 |
170 | if (!empty(array_intersect($protocols, ['ftp', 'ftps']))) {
171 | $pattern = '/(ftp|ftps)\:\/\/[-a-zA-Z0-9@:%_+.~#?&\/=]+(\/\S*)?/i';
172 | $url = preg_replace($pattern, "$0", $url);
173 | }
174 |
175 | if (in_array('mail', $protocols)) {
176 | $pattern = '/([^\s<]+?@[^\s<]+?\.[^\s<]+)(?$0", $url);
178 | }
179 | }
180 |
181 | return $url;
182 | }
183 |
184 |
185 | /**
186 | * 获取域名
187 | * @param string $url
188 | * @param bool $firstLevel 是否获取一级域名,如:abc.test.com取test.com
189 | * @param array $server server信息
190 | * @return string
191 | */
192 | public static function getDomain(string $url, bool $firstLevel = false, array $server = []): string {
193 | if (empty($server)) {
194 | $server = $_SERVER;
195 | }
196 | if (empty($url)) {
197 | $url = $server['HTTP_HOST'] ?? '';
198 | }
199 |
200 | if (!stripos($url, '://')) {
201 | $url = 'http://' . $url;
202 | }
203 |
204 | $parse = parse_url(strtolower($url));
205 | $domain = '';
206 | if (isset($parse['host'])) {
207 | $domain = $parse['host'];
208 | }
209 |
210 | if ($firstLevel) {
211 | $arr = explode('.', $domain);
212 | $size = count($arr);
213 | if ($size >= 2) {
214 | $domain = $arr[$size - 2] . '.' . end($arr);
215 | }
216 | }
217 |
218 | return $domain;
219 | }
220 |
221 |
222 | /**
223 | * 获取当前页面完整URL地址
224 | * @param array $server server信息
225 | * @return string
226 | */
227 | public static function getUrl(array $server = []): string {
228 | if (empty($server)) {
229 | $server = $_SERVER;
230 | }
231 |
232 | $host = $server['HTTP_HOST'] ?? '';
233 | $uri = $server['REQUEST_URI'] ?? '';
234 |
235 | $scheme = $server['HTTP_X_FORWARDED_PROTO'] ?? '';
236 | if (empty($scheme) && isset($server['HTTP_CF_VISITOR'])) {
237 | $obj = json_decode(strval($server['HTTP_CF_VISITOR']), true);
238 | if ($obj && is_object($obj)) {
239 | $scheme = $obj->scheme ?? '';
240 | }
241 | }
242 | if (empty($scheme)) {
243 | $scheme = $server['REQUEST_SCHEME'] ?? 'http';
244 | }
245 |
246 | if (empty($host)) {
247 | return '';
248 | }
249 |
250 | $res = "{$scheme}://{$host}{$uri}";
251 |
252 | return $res;
253 | }
254 |
255 |
256 | /**
257 | * 获取URI
258 | * @param array $server
259 | * @return string
260 | */
261 | public static function getUri(array $server = []): string {
262 | if (empty($server)) {
263 | $server = $_SERVER;
264 | }
265 |
266 | if (isset($server['REQUEST_URI'])) {
267 | return $server['REQUEST_URI'];
268 | }
269 |
270 | $uri = ($server['PHP_SELF'] ?? '') . "?" . ($server['QUERY_STRING'] ?? ($server['argv'][0] ?? ''));
271 | return $uri;
272 | }
273 |
274 |
275 | /**
276 | * 获取站点URL
277 | * @param string $url 网址
278 | * @param array $server server信息
279 | * @return string
280 | */
281 | public static function getSiteUrl(string $url, array $server = []): string {
282 | if (empty($url)) {
283 | $url = self::getUrl($server);
284 | }
285 |
286 | if (!stripos($url, '://')) {
287 | $url = 'http://' . $url;
288 | }
289 |
290 | if (!ValidateHelper::isUrl($url)) {
291 | return '';
292 | }
293 |
294 | $arr = parse_url($url);
295 | $port = $arr['port'] ?? null;
296 | $scheme = $arr['scheme'] ?? null;
297 | if (!empty($port) && !in_array($port, [80, 443])) {
298 | $url = "{$scheme}://{$arr['host']}:{$port}/";
299 | } else {
300 | $url = "{$scheme}://{$arr['host']}/";
301 | }
302 |
303 | $url = strtolower($url);
304 |
305 | return self::formatUrl($url);
306 | }
307 |
308 | }
--------------------------------------------------------------------------------
/src/Interfaces/Arrayable.php:
--------------------------------------------------------------------------------
1 | __datas = $datas;
51 | }
52 | }
53 |
54 |
55 | /**
56 | * 魔术get
57 | * @param $key
58 | * @return mixed|null
59 | */
60 | public function __get($key) {
61 | return $this->__datas[$key] ?? null;
62 | }
63 |
64 |
65 | /**
66 | * 魔术set
67 | * @param $key
68 | * @param $value
69 | */
70 | public function __set($key, $value) {
71 | $this->__datas[$key] = $value;
72 | }
73 |
74 |
75 | /**
76 | * 获取键值
77 | * @param $key
78 | * @return mixed|null
79 | */
80 | public function get($key) {
81 | return $this->__datas[$key] ?? null;
82 | }
83 |
84 |
85 | /**
86 | * 设置键值
87 | * @param $key
88 | * @param $value
89 | */
90 | public function set($key, $value): void {
91 | $this->__datas[$key] = $value;
92 | }
93 |
94 |
95 | /**
96 | * 键是否存在
97 | * @param mixed $offset
98 | * @return bool
99 | */
100 | public function offsetExists($offset): bool {
101 | return isset($this->__datas[$offset]);
102 | }
103 |
104 |
105 | /**
106 | * 获取键值
107 | * @param mixed $offset
108 | * @return mixed|null
109 | */
110 | public function offsetGet($offset) {
111 | return $this->__datas[$offset] ?? null;
112 | }
113 |
114 |
115 | /**
116 | * 设置键值
117 | * @param mixed $offset
118 | * @param mixed $value
119 | */
120 | public function offsetSet($offset, $value): void {
121 | $this->__datas[$offset] = $value;
122 | }
123 |
124 |
125 | /**
126 | * 删除键
127 | * @param mixed $offset
128 | */
129 | public function offsetUnset($offset): void {
130 | unset($this->__datas[$offset]);
131 | }
132 |
133 |
134 | /**
135 | * json序列化
136 | * @return array
137 | */
138 | public function jsonSerialize(): array {
139 | return $this->__datas;
140 | }
141 |
142 |
143 | /**
144 | * 序列化
145 | * @return string
146 | */
147 | public function serialize(): string {
148 | return serialize($this->__datas);
149 | }
150 |
151 |
152 | /**
153 | * 反序列化
154 | * @param string $serialized
155 | */
156 | public function unserialize($serialized): void {
157 | $this->__datas = unserialize($serialized);
158 | }
159 |
160 |
161 | /**
162 | * 计算数量
163 | * @return int
164 | */
165 | public function count(): int {
166 | return count($this->__datas);
167 | }
168 |
169 |
170 | /**
171 | * 当前元素
172 | * @return mixed
173 | */
174 | public function current() {
175 | return current($this->__datas);
176 | }
177 |
178 |
179 | /**
180 | * 下一个元素
181 | * @return mixed|void
182 | */
183 | public function next() {
184 | $this->__index++;
185 | return next($this->__datas);
186 | }
187 |
188 |
189 | /**
190 | * 获取当前元素的键
191 | * @return int|mixed|string|null
192 | */
193 | public function key() {
194 | return key($this->__datas);
195 | }
196 |
197 |
198 | /**
199 | * 验证当前位置
200 | * @return bool
201 | */
202 | public function valid(): bool {
203 | return count($this->__datas) >= $this->__index;
204 | }
205 |
206 |
207 | /**
208 | * 重置迭代器
209 | * @return mixed|void
210 | */
211 | public function rewind() {
212 | $this->__index = 0;
213 | return reset($this->__datas);
214 | }
215 |
216 |
217 | /**
218 | * 返回数组
219 | * @return array
220 | */
221 | public function toArray(): array {
222 | return $this->__datas;
223 | }
224 |
225 |
226 | /**
227 | * 返回json串
228 | * @param int $options
229 | * @param int $depth
230 | * @return mixed|string
231 | */
232 | public function toJson(int $options = 0, int $depth = 512): string {
233 | return json_encode($this->__datas, $options, $depth);
234 | }
235 |
236 |
237 | /**
238 | * 查找元素
239 | * @param $needle
240 | * @param bool $strict
241 | * @return false|int|string
242 | */
243 | public function search($needle, $strict = false) {
244 | return array_search($needle, $this->__datas, $strict);
245 | }
246 |
247 |
248 | /**
249 | * 元素位置
250 | * @param $needle
251 | * @return false|int|string
252 | */
253 | public function indexOf($needle) {
254 | return $this->search($needle);
255 | }
256 |
257 |
258 | /**
259 | * 元素最后出现位置
260 | * @param $needle
261 | * @return bool|int|string
262 | */
263 | public function lastIndexOf($needle) {
264 | $res = false;
265 | foreach ($this->__datas as $k => $it) {
266 | if ($needle == $it) {
267 | $res = $k;
268 | }
269 | }
270 |
271 | return $res;
272 | }
273 |
274 |
275 | /**
276 | * 删除元素(根据键)
277 | * @param $key
278 | * @return bool
279 | */
280 | public function delete($key): bool {
281 | $res = false;
282 | if (isset($this->__datas[$key])) {
283 | unset($this->__datas[$key]);
284 | $res = true;
285 | }
286 |
287 | return $res;
288 | }
289 |
290 |
291 | /**
292 | * 移除元素(根据值)
293 | * @param $value
294 | * @return ArrayObject
295 | */
296 | public function remove($value): ArrayObject {
297 | $key = $this->search($value);
298 | if ($key !== false) {
299 | unset($this->__datas[$key]);
300 | }
301 |
302 | return $this;
303 | }
304 |
305 |
306 | /**
307 | * 清空
308 | */
309 | public function clear(): void {
310 | $this->__datas = [];
311 | }
312 |
313 |
314 | /**
315 | * 是否包含值
316 | * @param $val
317 | * @return bool
318 | */
319 | public function contains($val): bool {
320 | return in_array($val, $this->__datas);
321 | }
322 |
323 |
324 | /**
325 | * 是否存在键
326 | * @param $key
327 | * @return bool
328 | */
329 | public function exists($key): bool {
330 | return array_key_exists($key, $this->__datas);
331 | }
332 |
333 |
334 | /**
335 | * 连接
336 | * @param string $str
337 | * @return string
338 | */
339 | public function join($str = ''): string {
340 | return implode($str, $this->__datas);
341 | }
342 |
343 |
344 | /**
345 | * 插入元素
346 | * @param int $offset 位置
347 | * @param mixed $val 元素值
348 | * @return bool
349 | */
350 | public function insert($offset, $val): bool {
351 | if ($offset > $this->count()) {
352 | return false;
353 | }
354 |
355 | array_splice($this->__datas, $offset, 0, $val);
356 |
357 | return true;
358 | }
359 |
360 |
361 | /**
362 | * 是否为空
363 | * @return bool
364 | */
365 | public function isEmpty(): bool {
366 | return empty($this->__datas);
367 | }
368 |
369 |
370 | /**
371 | * 求和
372 | * @return float|int
373 | */
374 | public function sum() {
375 | return array_sum($this->__datas);
376 | }
377 |
378 |
379 | /**
380 | * 求乘积
381 | * @return float|int
382 | */
383 | public function product() {
384 | return array_product($this->__datas);
385 | }
386 |
387 |
388 | /**
389 | * 用回调函数迭代地将数组简化为单一的值
390 | * @param callable $fn
391 | * @return mixed
392 | */
393 | public function reduce(callable $fn, $initial = null) {
394 | return array_reduce($this->__datas, $fn, $initial);
395 | }
396 |
397 |
398 | /**
399 | * 向数组尾部追加元素
400 | * @param $val
401 | * @return int
402 | */
403 | public function append($val): int {
404 | return array_push($this->__datas, $val);
405 | }
406 |
407 |
408 | /**
409 | * 向数组头部追加元素
410 | * @param $val
411 | * @return int
412 | */
413 | public function prepend($val): int {
414 | return array_unshift($this->__datas, $val);
415 | }
416 |
417 |
418 | /**
419 | * 从数组尾部弹出元素
420 | * @return mixed
421 | */
422 | public function pop() {
423 | return array_pop($this->__datas);
424 | }
425 |
426 |
427 | /**
428 | * 从数组头部弹出元素
429 | * @return mixed
430 | */
431 | public function shift() {
432 | return array_shift($this->__datas);
433 | }
434 |
435 |
436 | /**
437 | * 数组切片
438 | * @param $offset
439 | * @param null $length
440 | * @return ArrayObject
441 | */
442 | public function slice($offset, $length = null): ArrayObject {
443 | return new self(array_slice($this->__datas, $offset, $length));
444 | }
445 |
446 |
447 | /**
448 | * 随机获取一个元素
449 | * @return mixed|null
450 | */
451 | public function rand() {
452 | $key = array_rand($this->__datas, 1);
453 | return $this->__datas[$key] ?? null;
454 | }
455 |
456 |
457 | /**
458 | * 遍历数组
459 | * @param callable $fn 处理函数
460 | * @return ArrayObject
461 | */
462 | public function each(callable $fn): ArrayObject {
463 | if ($this->count() > 0) {
464 | array_walk($this->__datas, $fn);
465 | }
466 |
467 | return $this;
468 | }
469 |
470 |
471 | /**
472 | * 遍历数组,并构建新数组
473 | * @param callable $fn
474 | * @return ArrayObject
475 | */
476 | public function map(callable $fn): ArrayObject {
477 | return new self(array_map($fn, $this->__datas));
478 | }
479 |
480 |
481 | /**
482 | * 所有值
483 | * @return ArrayObject
484 | */
485 | public function values(): ArrayObject {
486 | return new self(array_values($this->__datas));
487 | }
488 |
489 |
490 | /**
491 | * 所有键
492 | * @param null $search_value
493 | * @param bool $strict
494 | * @return ArrayObject
495 | */
496 | public function keys($search_value = null, $strict = false): ArrayObject {
497 | if (is_null($search_value)) {
498 | $keys = array_keys($this->__datas);
499 | } else {
500 | $keys = array_keys($this->__datas, $search_value, $strict);
501 | }
502 |
503 | return new self($keys);
504 | }
505 |
506 |
507 | /**
508 | * 返回列
509 | * @param $column_key
510 | * @param null $index
511 | * @return ArrayObject
512 | */
513 | public function column($column_key, $index = null): ArrayObject {
514 | return new self(array_column($this->__datas, $column_key, $index));
515 | }
516 |
517 |
518 | /**
519 | * 去重
520 | * @param int $sort_flags
521 | * @return ArrayObject
522 | */
523 | public function unique($sort_flags = SORT_STRING): ArrayObject {
524 | return new self(array_unique($this->__datas, $sort_flags));
525 | }
526 |
527 |
528 | /**
529 | * 获取重复的元素
530 | * @param int $sort_flags
531 | * @return ArrayObject
532 | */
533 | public function multiple($sort_flags = SORT_STRING): ArrayObject {
534 | $arr = array_unique($this->__datas, $sort_flags);
535 | return new self(array_merge(array_diff_assoc($this->__datas, $arr)));
536 | }
537 |
538 |
539 | /**
540 | * 排序
541 | * @param int $sort_flags
542 | * @return ArrayObject
543 | */
544 | public function sort($sort_flags = SORT_REGULAR): ArrayObject {
545 | sort($this->__datas, $sort_flags);
546 |
547 | return $this;
548 | }
549 |
550 |
551 | /**
552 | * 反序
553 | * @param bool $preserve_keys
554 | * @return ArrayObject
555 | */
556 | public function reverse($preserve_keys = false): ArrayObject {
557 | $this->__datas = array_reverse($this->__datas, $preserve_keys);
558 |
559 | return $this;
560 | }
561 |
562 |
563 | /**
564 | * 乱序
565 | * @return ArrayObject
566 | */
567 | public function shuffle(): ArrayObject {
568 | if ($this->count() > 0) {
569 | shuffle($this->__datas);
570 | }
571 |
572 | return $this;
573 | }
574 |
575 |
576 | /**
577 | * 将一个数组分割成多个数组
578 | * @param $size
579 | * @param bool $preserve_keys 是否保留键名
580 | * @return ArrayObject
581 | */
582 | public function chunk($size, $preserve_keys = false): ArrayObject {
583 | return new self(array_chunk($this->__datas, $size, $preserve_keys));
584 | }
585 |
586 |
587 | /**
588 | * 交换数组中的键和值
589 | * @return ArrayObject
590 | */
591 | public function flip(): ArrayObject {
592 | return new self(array_flip($this->__datas));
593 | }
594 |
595 |
596 | /**
597 | * 过滤数组中的元素
598 | * @param callable $fn
599 | * @param int $flag
600 | * @return ArrayObject
601 | */
602 | public function filter(callable $fn, $flag = 0): ArrayObject {
603 | return new self(array_filter($this->__datas, $fn, $flag));
604 | }
605 |
606 |
607 | }
--------------------------------------------------------------------------------
/src/Objects/BaseObject.php:
--------------------------------------------------------------------------------
1 | getMethods() : $class->getMethods($filter);
127 | if (!empty($methods)) {
128 | foreach ($methods as $methodObj) {
129 | array_push($res, $methodObj->name);
130 | }
131 |
132 | //不包括父类的方法
133 | if (!$includeParent && $parentClass = get_parent_class($name)) {
134 | $parentMethods = get_class_methods($parentClass);
135 | if (!empty($parentMethods)) {
136 | $res = array_diff($res, $parentMethods);
137 | }
138 | }
139 | }
140 |
141 | return $res;
142 | }
143 |
144 |
145 | /**
146 | * 实例化并返回[调用此方法的类,静态绑定]
147 | * 不可重写
148 | * @return object
149 | */
150 | final public static function getSelfInstance(): object {
151 | if (is_null(static::$_self) || !is_object(static::$_self) || !(static::$_self instanceof static)) {
152 | //静态延迟绑定
153 | static::$_self = new static();
154 | }
155 |
156 | return static::$_self;
157 | }
158 |
159 |
160 | /**
161 | * 是否存在当前[调用]类实例化
162 | * 不可重写
163 | * @return bool
164 | */
165 | final public static function hasSelfInstance(): bool {
166 | return isset(static::$_self);
167 | }
168 |
169 |
170 | /**
171 | * 销毁当前[调用]类实例化
172 | * 不可重写
173 | */
174 | final public static function destroySelfInstance(): void {
175 | static::$_self = null;
176 | }
177 |
178 |
179 | /**
180 | * 实例化并返回[最终父类]
181 | * 可重写,返回所重写的类
182 | * 当前返回BaseObject
183 | * @return BaseObject
184 | */
185 | public static function getFinalInstance(): BaseObject {
186 | if (is_null(self::$_final) || !is_object(self::$_final) || !(self::$_final instanceof self)) {
187 | self::$_final = new self();
188 | }
189 |
190 | return self::$_final;
191 | }
192 |
193 |
194 | /**
195 | * 是否存在最终类实例化
196 | * 可重写
197 | * @return bool
198 | */
199 | public static function hasFinalInstance(): bool {
200 | return isset(self::$_final);
201 | }
202 |
203 |
204 | /**
205 | * 销毁最终类实例化
206 | * 可重写
207 | */
208 | public static function destroyFinalInstance(): void {
209 | self::$_final = null;
210 | }
211 |
212 |
213 | }
--------------------------------------------------------------------------------
/src/Objects/StrictObject.php:
--------------------------------------------------------------------------------
1 | $value) {
58 | $this->set($field, $value);
59 | }
60 | }
61 |
62 |
63 | /**
64 | * 获取该类的反射对象
65 | * @return ReflectionClass
66 | * @throws ReflectionException
67 | */
68 | public function getReflectionObject() {
69 | if (is_null($this->__refCls)) {
70 | $this->__refCls = new ReflectionClass($this);
71 | }
72 |
73 | return $this->__refCls;
74 | }
75 |
76 |
77 | /**
78 | * 获取未定义时警告
79 | * @param string $name 名称
80 | * @throws Throwable
81 | */
82 | protected function __undefinedGetWarn(string $name) {
83 | throw new Exception('Undefined readable property: ' . static::class . '::' . $name);
84 | }
85 |
86 |
87 | /**
88 | * 设置未定义时警告
89 | * @param string $name
90 | * @throws Throwable
91 | */
92 | protected function __undefinedSetWarn(string $name) {
93 | throw new Exception('Undefined writable property: ' . static::class . '::' . $name);
94 | }
95 |
96 |
97 | /**
98 | * 检查是否空属性
99 | * @param string $name
100 | * @throws Throwable
101 | */
102 | protected function __checkEmptyProperty(string $name) {
103 | if (is_null($name) || trim($name) === '') {
104 | throw new Exception('empty property: ' . static::class . '::');
105 | }
106 | }
107 |
108 |
109 | /**
110 | * 获取属性值或调用获取方法,如 get(name) => getName()
111 | * @param string $name
112 | * @return mixed|void
113 | * @throws Throwable
114 | */
115 | final public function get(string $name) {
116 | $this->__checkEmptyProperty($name);
117 |
118 | // 获取public、protected属性
119 | if (property_exists($this, $name)) {
120 | try {
121 | return $this->$name;
122 | } catch (Error $e) {
123 | }
124 | }
125 |
126 | // 对private属性,调用getXXX方法
127 | $methodName = 'get' . ucfirst($name);
128 | if (method_exists($this, $methodName)) {
129 | try {
130 | return $this->$methodName();
131 | } catch (Error $e) {
132 | }
133 | }
134 |
135 | return $this->__undefinedGetWarn($name);
136 | }
137 |
138 |
139 | /**
140 | * 设置属性值或调用设置方法,如 set(name,val) => setName(val)
141 | * @param string $name
142 | * @param mixed $value
143 | * @return bool|void
144 | * @throws Throwable
145 | */
146 | final public function set(string $name, $value = null) {
147 | $this->__checkEmptyProperty($name);
148 |
149 | // 设置public、protected属性
150 | if (property_exists($this, $name)) {
151 | try {
152 | $this->$name = $value;
153 | return true;
154 | } catch (Error $e) {
155 | }
156 | }
157 |
158 | // 对private属性,调用setXXX方法
159 | $methodName = 'set' . ucfirst($name);
160 | if (method_exists($this, $methodName)) {
161 | try {
162 | $this->$methodName($value);
163 | return true;
164 | } catch (Error $e) {
165 | }
166 | }
167 |
168 | //从销毁字段中移除
169 | $idx = array_search($name, $this->unsetFields);
170 | if ($idx != false) {
171 | unset($this->unsetFields[$name]);
172 | }
173 |
174 | return $this->__undefinedSetWarn($name);
175 | }
176 |
177 |
178 | /**
179 | * 属性是否存在(包括NULL值)
180 | * @param $name
181 | * @return bool
182 | */
183 | public function isset($name): bool {
184 | if (is_null($name) || $name == '') {
185 | return false;
186 | }
187 |
188 | $res = isset($this->$name); //不能检查null值
189 | if (!$res && !in_array($name, $this->unsetFields)) {
190 | $fields = get_class_vars($this);
191 | $res = array_key_exists($name, $fields);
192 | }
193 |
194 | return $res;
195 | }
196 |
197 |
198 | /**
199 | * 销毁属性
200 | * @param $name
201 | */
202 | public function unset($name): void {
203 | if (is_null($name) || $name == '') {
204 | return;
205 | }
206 |
207 | if (is_null($this->jsonFields)) {
208 | $this->getJsonFields();
209 | }
210 |
211 | unset($this->$name);
212 | array_push($this->unsetFields, $name);
213 | }
214 |
215 |
216 | /**
217 | * 获取可json字段
218 | * @return array
219 | * @throws ReflectionException
220 | */
221 | protected function getJsonFields(): array {
222 | if (is_null($this->jsonFields)) {
223 | $this->jsonFields = [];
224 | $ref = $this->getReflectionObject();
225 | array_map(function (ReflectionProperty $fieldObj) {
226 | array_push($this->jsonFields, $fieldObj->getName());
227 | }, array_filter($ref->getProperties(ReflectionProperty::IS_PROTECTED | ReflectionProperty::IS_PUBLIC), function (ReflectionProperty $field) {
228 | return !$field->isStatic();
229 | }));
230 | }
231 |
232 | return $this->jsonFields;
233 | }
234 |
235 |
236 | /**
237 | * json序列化(仅public、protected的属性)
238 | * @return array
239 | * @throws ReflectionException
240 | */
241 | public function jsonSerialize(): array {
242 | $arr = [];
243 | $fields = $this->getJsonFields();
244 | if (!empty($fields)) {
245 | array_map(function ($field) use (&$arr) {
246 | $arr[$field] = $this->{$field};
247 | }, array_filter($fields, function (string $field) {
248 | //过滤已销毁的属性
249 | return $this->isset($field);
250 | }));
251 | }
252 | unset($fields);
253 |
254 | return $arr;
255 | }
256 |
257 |
258 | /**
259 | * 转为数组
260 | * @return array
261 | * @throws ReflectionException
262 | */
263 | public function toArray(): array {
264 | return $this->jsonSerialize();
265 | }
266 |
267 |
268 | /**
269 | * 转为json串
270 | * @param int $options
271 | * @param int $depth
272 | * @return string
273 | * @throws ReflectionException
274 | */
275 | public function toJson(int $options = 0, int $depth = 512): string {
276 | return json_encode($this->jsonSerialize(), $options, $depth);
277 | }
278 |
279 |
280 | }
--------------------------------------------------------------------------------
/src/Services/BaseService.php:
--------------------------------------------------------------------------------
1 | errno);
69 | }
70 |
71 |
72 | /**
73 | * 获取错误信息
74 | * @return string
75 | */
76 | public function getError(): string {
77 | $res = strval($this->error);
78 | if ($this->errno && empty($res)) {
79 | $res = Consts::UNKNOWN;
80 | }
81 |
82 | return $res;
83 | }
84 |
85 |
86 | /**
87 | * 设置服务错误信息
88 | * @param string $error 错误信息
89 | * @param int|mixed $errno 错误代码
90 | */
91 | public function setErrorInfo(string $error = '', $errno = null) {
92 | if ($error) {
93 | $this->error = $error;
94 | }
95 |
96 | if ($errno) {
97 | $this->errno = intval($errno);
98 | }
99 | }
100 |
101 |
102 | /**
103 | * 获取服务错误信息
104 | * @return array
105 | */
106 | public function getErrorInfo(): array {
107 | return [
108 | 'errno' => $this->getErrno(),
109 | 'error' => $this->getError(),
110 | ];
111 | }
112 |
113 |
114 | /**
115 | * 设置结果
116 | * @param mixed $arr
117 | * @return void
118 | */
119 | public function setResult($arr): void {
120 | $this->result = $arr;
121 | }
122 |
123 |
124 | /**
125 | * 获取结果
126 | * @return mixed
127 | */
128 | public function getResult() {
129 | return $this->result;
130 | }
131 |
132 | }
--------------------------------------------------------------------------------
/src/Util/MacAddress.php:
--------------------------------------------------------------------------------
1 | nick;
30 | }
31 |
32 | protected function setNick(string $nick) {
33 | $this->nick = $nick;
34 | }
35 |
36 | protected function setId($id) {
37 | $this->id = $id;
38 | }
39 |
40 | protected function getId() {
41 | return $this->id;
42 | }
43 |
44 |
45 | private function setNo($no) {
46 | $this->no = $no;
47 | }
48 |
49 |
50 | private function getNo() {
51 | return $this->no;
52 | }
53 |
54 |
55 | protected static function hello() {
56 | return 'hello';
57 | }
58 |
59 |
60 | public static function world() {
61 | return 'world';
62 | }
63 |
64 |
65 | }
--------------------------------------------------------------------------------
/tests/README.md:
--------------------------------------------------------------------------------
1 | # test
2 | e.g.
3 |
4 | phpunit --bootstrap bootstrap.php --repeat 100 XxxTest.php
5 |
6 | phpunit --repeat 100 XxxTest.php
7 |
--------------------------------------------------------------------------------
/tests/Unit/ConvertHelperTest.php:
--------------------------------------------------------------------------------
1 | id = $i;
26 | $chi->type = 'child';
27 | $chi->name = 'boy-' . strval($i);
28 | $chi->childs = [];
29 |
30 | array_push($childs, $chi);
31 | }
32 |
33 | $par = new \stdClass();
34 | $par->id = 0;
35 | $par->type = 'parent';
36 | $par->name = 'hello';
37 | $par->childs = $childs;
38 | $par->one = current($childs);
39 |
40 | $res1 = ConvertHelper::object2Array(new \stdClass());
41 | $this->assertEmpty($res1);
42 |
43 | $res2 = ConvertHelper::object2Array($chi);
44 | $this->assertEquals(4, count($res2));
45 |
46 | $res3 = ConvertHelper::object2Array($par);
47 | $this->assertEquals(4, count($res3['childs']));
48 |
49 | $res4 = ConvertHelper::object2Array(1);
50 | $this->assertEquals(1, count($res4));
51 |
52 | $obj = new \stdClass();
53 | $obj->childs = array_fill(0, 5, $par);
54 | $res5 = ConvertHelper::object2Array($obj);
55 | $this->assertTrue(is_array($res5['childs'][0]));
56 | }
57 |
58 |
59 | public function testArrayToObject() {
60 | $arr = [
61 | 'aa' => [
62 | 'id' => 9,
63 | 'age' => 19,
64 | 'name' => 'hello',
65 | 'child' => [],
66 | ],
67 | 'bb' => [
68 | 'id' => 2,
69 | 'age' => 31,
70 | 'name' => 'lizz',
71 | ],
72 | 'cc' => [
73 | 'id' => 9,
74 | 'age' => 19,
75 | 'name' => 'hello',
76 | ],
77 | 'dd' => [
78 | 'id' => 87,
79 | 'age' => 50,
80 | 'name' => 'zhang3',
81 | ],
82 | ];
83 |
84 | $res1 = ConvertHelper::array2Object([]);
85 | $this->assertTrue(is_object($res1));
86 |
87 | $res2 = ConvertHelper::array2Object($arr);
88 | $this->assertTrue(is_object($res2->aa->child));
89 | }
90 |
91 |
92 | public function testStr2hex() {
93 | $str1 = 'hello';
94 | $res1 = ConvertHelper::str2hex($str1);
95 |
96 | $str2 = "1+2=3\r";
97 | $res2 = ConvertHelper::str2hex($str2);
98 |
99 | $this->assertEquals('68656c6c6f', $res1);
100 | $this->assertEquals('312b323d330d', $res2);
101 | }
102 |
103 |
104 | public function testHex2Str() {
105 | $str = '68656c6c6f';
106 | $res = ConvertHelper::hex2Str($str);
107 | $this->assertEquals('hello', $res);
108 | }
109 |
110 |
111 |
112 | }
--------------------------------------------------------------------------------
/tests/Unit/DebugHelperTest.php:
--------------------------------------------------------------------------------
1 | assertTrue(file_exists($logFile));
37 | }
38 |
39 | }
--------------------------------------------------------------------------------
/tests/Unit/DirectoryHelperTest.php:
--------------------------------------------------------------------------------
1 | assertTrue($res);
25 |
26 | $res = DirectoryHelper::mkdirDeep('');
27 | $this->assertFalse($res);
28 |
29 | $res = DirectoryHelper::mkdirDeep($dir);
30 | $this->assertTrue($res);
31 |
32 | DirectoryHelper::mkdirDeep('/root/hello');
33 | }
34 |
35 |
36 | public function testGetFileTree() {
37 | $all = DirectoryHelper::getFileTree(TESTDIR);
38 | $oth = DirectoryHelper::getFileTree(TESTDIR, 'unknow');
39 | $dirs = DirectoryHelper::getFileTree(TESTDIR, 'dir');
40 | $files = DirectoryHelper::getFileTree(TESTDIR, 'file');
41 | $one = DirectoryHelper::getFileTree(__FILE__);
42 |
43 | $this->assertEquals(count($all), count($oth));
44 | $this->assertEquals(count($all), count($dirs) + count($files));
45 | $this->assertEquals(1, count($one));
46 |
47 | DirectoryHelper::getFileTree(TESTDIR, 'file', true);
48 | }
49 |
50 |
51 | public function testGetDirSize() {
52 | $res = DirectoryHelper::getDirSize(TESTDIR);
53 | $this->assertGreaterThan(1, $res);
54 |
55 | $res = DirectoryHelper::getDirSize('');
56 | $this->assertEquals(0, $res);
57 | }
58 |
59 |
60 | public function testCopyDirclearDirDelDir() {
61 | $baseDir = TESTDIR . 'tmp/backup/ab';
62 | $backupDir1 = $baseDir . '/1';
63 | $backupDir2 = $baseDir . '/2';
64 | DirectoryHelper::chmodBatch($backupDir1, 766, 766);
65 | DirectoryHelper::chmodBatch($backupDir2, 766, 766);
66 |
67 | $fromDir = dirname(TESTDIR) . '/src';
68 | $res1 = DirectoryHelper::copyDir($fromDir, $backupDir1);
69 | $res2 = DirectoryHelper::copyDir($fromDir, $backupDir1, true);
70 | $res3 = DirectoryHelper::copyDir($fromDir, $backupDir2);
71 | $res4 = DirectoryHelper::copyDir($fromDir, $backupDir2, true);
72 | $res5 = DirectoryHelper::copyDir($fromDir, $backupDir2, false);
73 |
74 | $this->assertTrue($res1);
75 | $this->assertTrue($res2);
76 | $this->assertTrue($res3);
77 | $this->assertTrue($res4);
78 | $this->assertTrue($res5);
79 |
80 | DirectoryHelper::chmodBatch('', 777, 777);
81 | DirectoryHelper::chmodBatch('/root', 777, 777);
82 | DirectoryHelper::chmodBatch($backupDir1, 777, 777);
83 |
84 | //clearDir
85 | $res5 = DirectoryHelper::clearDir('');
86 | $res6 = DirectoryHelper::clearDir($backupDir1);
87 | $this->assertFalse($res5);
88 | $this->assertTrue($res6);
89 | $this->assertTrue(is_dir($backupDir1));
90 |
91 | //delDir
92 | $res7 = DirectoryHelper::delDir('');
93 | $res8 = DirectoryHelper::delDir($backupDir2);
94 | $this->assertFalse($res7);
95 | $this->assertTrue($res8);
96 | $this->assertFalse(is_dir($backupDir2));
97 |
98 | DirectoryHelper::copyDir($fromDir, '/root/tmp');
99 | // $files1 = DirectoryHelper::getFileTree($backupDir1);
100 | // $files2 = DirectoryHelper::getFileTree($backupDir2);
101 | // $this->assertEquals(0, count($files1));
102 | // $this->assertEquals(0, count($files2));
103 | }
104 |
105 |
106 | public function testFormatDir() {
107 | $res1 = DirectoryHelper::formatDir('');
108 | $res2 = DirectoryHelper::formatDir('/usr|///tmp:\\\123/\abc<|\hello>\/%world?\\how$\\are');
109 |
110 | $this->assertEmpty($res1);
111 | $this->assertEquals('/usr/tmp/123/abc/hello/%world/how$/are/', $res2);
112 | }
113 |
114 |
115 | }
--------------------------------------------------------------------------------
/tests/Unit/EncryptHelperTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($res1, 'aHR0cHM6Ly90b29sLmdvb2dsZS5jb20ubmV0L2VuY3J5cHQ_dHlwZT00SGVsbG8gV29ybGQhIOS9oOWlve-8gQ');
32 |
33 | $res2 = EncryptHelper::base64UrlDecode($res1);
34 | $this->assertEquals($res2, $str);
35 | }
36 |
37 |
38 | public function testAuthcode() {
39 | $origin = 'hello world!';
40 | $key = '123456';
41 |
42 | $enres = EncryptHelper::authcode($origin, $key, true, Consts::TTL_ONE_YEAR);
43 | $deres = EncryptHelper::authcode($enres[0], $key, false);
44 | $this->assertEquals($origin, $deres[0]);
45 | $this->assertEquals($enres[1], $deres[1]);
46 |
47 | $res1 = EncryptHelper::authcode('', '', true);
48 | $res2 = EncryptHelper::authcode('', '', false);
49 | $this->assertEquals('', $res1[0]);
50 | $this->assertEquals('', $res2[0]);
51 |
52 | $res3 = EncryptHelper::authcode('hello', $key, false);
53 | $this->assertEquals('', $res3[0]);
54 |
55 | $res4 = EncryptHelper::authcode('681ff2aaPIUK-k3oHs4StYD', $key, false);
56 | $this->assertEquals('', $res4[0]);
57 |
58 | $enres = EncryptHelper::authcode(self::$strHello, self::$emptyMd5, true, 0);
59 | //res:8c9eb7905a6SdXZfm-GoJpYKu6CzMgF0I-7neF-x3UKIUpYuIZSnK_2ZqaYSZlZw0Ofzwa2Bn0QZ6b4SLzSz
60 | $deres = EncryptHelper::authcode($enres[0], self::$emptyMd5, false);
61 | $this->assertEquals(self::$strHello, $deres[0]);
62 | $this->assertEquals($enres[1], $deres[1]);
63 |
64 | $enres = EncryptHelper::authcode(self::$strHelloEmoji, self::$emptyMd5, true, 0);
65 | //res:b42374af3DqX22zi207OJXsz6xP2vEXto39TPK_UzcJOdDZV0kQHPUFm5JOw-aWISFi0snglsrYtp5tpYGRuhgw50TPY8UnFSf912uZI38vGON0KHqAgCatmtdoBZ4VJI6IkHio-JLxbt8hkuCz1HCOElUkZxBMnGUle
66 | $deres = EncryptHelper::authcode($enres[0], self::$emptyMd5, false);
67 | $this->assertEquals(self::$strHelloEmoji, $deres[0]);
68 | $this->assertEquals($enres[1], $deres[1]);
69 |
70 | $key = substr(self::$emptyMd5, 0, 16);
71 | $enres = EncryptHelper::authcode(self::$strJson, $key, true, 0);
72 | //res:52a0945eK4NyxvnjEBnPlToROzO4KLKE9VvrqtxAiLPVPDK-HkvzahyMbxydmSifc3TQIo4mbsi9gzq7vbJ64YzpB_DP
73 | $deres = EncryptHelper::authcode($enres[0], $key, false);
74 | $this->assertEquals(self::$strJson, $deres[0]);
75 | $this->assertEquals($enres[1], $deres[1]);
76 | }
77 |
78 |
79 | public function testEasyEncryptDecrypt() {
80 | $origin = 'hello world!你好,世界!';
81 | $key = '123456';
82 |
83 | $enres = EncryptHelper::easyEncrypt($origin, $key);
84 | $deres = EncryptHelper::easyDecrypt($enres, $key);
85 | $this->assertEquals($origin, $deres);
86 |
87 | $res1 = EncryptHelper::easyEncrypt('', $key);
88 | $this->assertEquals('', $res1);
89 |
90 | $res2 = EncryptHelper::easyDecrypt('', $key);
91 | $this->assertEquals('', $res2);
92 |
93 | $res3 = EncryptHelper::easyDecrypt('0adc39zZaczdODqqimpcaCGfYBRwciJPLxFO3NTce8VfS5', $key);
94 | $this->assertEquals('', $res3);
95 |
96 | $res4 = EncryptHelper::easyDecrypt('e10adc39 ', $key);
97 | $this->assertEquals('', $res4);
98 |
99 | $str = implode('', range(0, 99));
100 | $res5 = EncryptHelper::easyEncrypt($str, $key);
101 | $res6 = EncryptHelper::easyDecrypt($res5, $key);
102 | $this->assertEquals($str, $res6);
103 | }
104 |
105 |
106 | public function testMurmurhash3Int() {
107 | $origin = 'hello';
108 | $res1 = EncryptHelper::murmurhash3Int($origin);
109 | $res2 = EncryptHelper::murmurhash3Int($origin, 3, false);
110 | $this->assertEquals(11, strlen($res1));
111 | $this->assertEquals(10, strlen($res2));
112 |
113 | $origin .= '2';
114 | $res3 = EncryptHelper::murmurhash3Int($origin);
115 | $origin .= '3';
116 | $res4 = EncryptHelper::murmurhash3Int($origin);
117 | }
118 |
119 |
120 | public function testOpensslEncryptDecrypt() {
121 | $str = 'hello world.';
122 | $key = 'Ti*1@^LSxg1E#^Gc';
123 | $iv = '37nCVPl5HtTKYBqW';
124 |
125 | $res0 = EncryptHelper::opensslEncrypt('', '');
126 | $res1 = EncryptHelper::opensslDecrypt('', '');
127 | $this->assertEmpty($res0);
128 | $this->assertEmpty($res1);
129 |
130 | $cipherText1 = EncryptHelper::opensslEncrypt($str, $key);
131 | $cipherText2 = EncryptHelper::opensslEncrypt($str, $key, $iv);
132 | $cipherText3 = EncryptHelper::opensslEncrypt($str, $key, $iv, 'aes-256-cbc');
133 | $cipherText4 = EncryptHelper::opensslEncrypt($str, $key, $iv, 'des-ede3-cbc');
134 |
135 | $clearText1 = EncryptHelper::opensslDecrypt($cipherText1, $key);
136 | $clearText2 = EncryptHelper::opensslDecrypt($cipherText2, $key, $iv);
137 | $clearText3 = EncryptHelper::opensslDecrypt($cipherText3, $key, $iv, 'aes-256-cbc');
138 | $clearText4 = EncryptHelper::opensslDecrypt($cipherText4, $key, $iv, 'des-ede3-cbc');
139 |
140 | $this->assertEquals($str, $clearText1);
141 | $this->assertEquals($str, $clearText2);
142 | $this->assertEquals($str, $clearText3);
143 | $this->assertEquals($str, $clearText4);
144 |
145 | }
146 |
147 |
148 | }
--------------------------------------------------------------------------------
/tests/Unit/FileHelperTest.php:
--------------------------------------------------------------------------------
1 | assertEquals('php', $ext1);
25 | $this->assertEquals('jpg', $ext2);
26 | }
27 |
28 |
29 | public function testWriteFile() {
30 | $file = TESTDIR . 'tmp/abc/test.log';
31 | $res = FileHelper::writeFile($file, 'hello world');
32 | $this->assertTrue($res);
33 | $this->assertTrue(file_exists($file));
34 | }
35 |
36 |
37 | public function testRemoveBom() {
38 | $file = TESTDIR . 'data/bom.txt';
39 | $str = file_get_contents($file);
40 | $len1 = strlen($str);
41 |
42 | $res = FileHelper::removeBom($str);
43 | $len2 = strlen($res);
44 |
45 | $this->assertEquals(3, ($len1 - $len2));
46 | }
47 |
48 |
49 | public function testCreateZip() {
50 | $files = [
51 | TESTDIR . 'tmp/abc/test.log',
52 | TESTDIR . '../src',
53 | TESTDIR . '../vendor',
54 | ];
55 | $dest = TESTDIR . 'tmp/test.zip';
56 | $dest2 = TESTDIR . 'tmp/hello/test.zip';
57 |
58 | $res1 = FileHelper::createZip($files, $dest, true);
59 | $res2 = FileHelper::createZip($files, $dest, false);
60 | $res3 = FileHelper::createZip([], $dest2, false);
61 | $res4 = FileHelper::createZip($files, $dest2, true);
62 | $this->assertTrue($res1);
63 | $this->assertFalse($res2);
64 | $this->assertFalse($res3);
65 | $this->assertFalse($res4);
66 | FileHelper::createZip($files, '/root/tmp/test.zip', true);
67 | }
68 |
69 |
70 | public function testImg2Base64() {
71 | $img = TESTDIR . 'data/php_elephant.png';
72 | $str = FileHelper::img2Base64($img);
73 | $this->assertNotEmpty($str);
74 | $this->assertGreaterThan(1, strpos($str, 'png'));
75 |
76 | $img = TESTDIR . 'data/png.webp';
77 | $str = FileHelper::img2Base64($img);
78 | $this->assertGreaterThan(1, strpos($str, 'webp'));
79 |
80 | $img = TESTDIR . 'data/banana.gif';
81 | $str = FileHelper::img2Base64($img);
82 | $this->assertGreaterThan(1, strpos($str, 'gif'));
83 |
84 | $img = TESTDIR . 'data/green.jpg';
85 | $str = FileHelper::img2Base64($img);
86 | $this->assertGreaterThan(1, strpos($str, 'jpeg'));
87 |
88 | $str = FileHelper::img2Base64('');
89 | $this->assertEmpty($str);
90 | }
91 |
92 |
93 | public function testGetAllMimes() {
94 | $res = FileHelper::getAllMimes();
95 | $this->assertGreaterThan(1, count($res));
96 | }
97 |
98 |
99 | public function testGetFileMime() {
100 | $img1 = TESTDIR . 'data/png.webp';
101 | $img2 = TESTDIR . 'data/php-logo.svg';
102 | $mim1 = FileHelper::getFileMime($img1);
103 | $mim2 = FileHelper::getFileMime($img2);
104 |
105 | $this->assertNotEmpty($mim1);
106 | $this->assertNotEmpty($mim2);
107 | }
108 |
109 |
110 | public function testReadInArray() {
111 | $file = TESTDIR . 'data/bom.txt';
112 | $arr = FileHelper::readInArray($file);
113 | $this->assertGreaterThan(1, count($arr));
114 |
115 | $arr = FileHelper::readInArray('/tmp/hello/1234');
116 | $this->assertEmpty($arr);
117 | }
118 |
119 |
120 | public function testFormatPath() {
121 | $res1 = FileHelper::formatPath('');
122 | $res2 = FileHelper::formatPath('/usr|///tmp:\\\123/\abc<|\hello>\/%world?\\how$\\are\@#test.png');
123 |
124 | $this->assertEmpty($res1);
125 | $this->assertEquals('/usr/tmp/123/abc/hello/%world/how$/are/@#test.png', $res2);
126 | }
127 |
128 |
129 | public function testGetAbsPath() {
130 | $path1 = 'ArrayHelperTest.php';
131 | $path2 = './Unit/DateHelperTest.php';
132 | $path3 = '../../docs/changelog.md';
133 | $path4 = '../../../.gitignore';
134 | $path5 = '/var/www/site';
135 |
136 | $res0 = FileHelper::getAbsPath('');
137 | $res1 = FileHelper::getAbsPath($path1, __DIR__);
138 | $res2 = FileHelper::getAbsPath($path2, TESTDIR);
139 | $res3 = FileHelper::getAbsPath($path3, __DIR__);
140 | $res4 = FileHelper::getAbsPath($path4);
141 | $res5 = FileHelper::getAbsPath($path5);
142 |
143 | $this->assertEmpty($res0);
144 | $this->assertTrue(file_exists($res1));
145 | $this->assertTrue(file_exists($res2));
146 | $this->assertTrue(file_exists($res3));
147 | $this->assertFalse(file_exists($res4));
148 | $this->assertEquals($path5, $res5);
149 | }
150 |
151 |
152 | public function testGetRelativePath() {
153 | $f1 = '/var/www/php/ci/a.php';
154 | $f2 = '/usr/local/php/log/test.log';
155 | $f3 = '/var/www/php/zf/b.php';
156 | $f4 = '/var/www/img/a.php';
157 | $f5 = '/var/www/api/img/b.php';
158 |
159 | $res1 = FileHelper::getRelativePath($f1, '');
160 | $res2 = FileHelper::getRelativePath($f1, $f2);
161 | $res3 = FileHelper::getRelativePath($f1, $f3);
162 | $res4 = FileHelper::getRelativePath($f4, $f5);
163 | $res5 = FileHelper::getRelativePath($f5, $f4);
164 |
165 | $this->assertEquals($res1, $f1);
166 | $this->assertEquals($res2, $f1);
167 | $this->assertEquals($res3, '../../ci/a.php');
168 | $this->assertEquals($res4, '../../../img/a.php');
169 | $this->assertEquals($res5, '../../api/img/b.php');
170 | }
171 |
172 |
173 | }
--------------------------------------------------------------------------------
/tests/Unit/MacAddressTest.php:
--------------------------------------------------------------------------------
1 | assertNotEmpty($res0);
26 | $this->assertEmpty($res2);
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/tests/Unit/NumberHelperTest.php:
--------------------------------------------------------------------------------
1 | assertEquals('0B', $res1);
30 | $this->assertEquals('1000KB', $res2);
31 | $this->assertEquals('976.563MB', $res3);
32 | $this->assertEquals('953.6743GB', $res4);
33 | }
34 |
35 |
36 | public function testInRange() {
37 | $res1 = NumberHelper::inRange(3, 9, 12);
38 | $res2 = NumberHelper::inRange(68, 12, 132);
39 | $res3 = NumberHelper::inRange(3.14159, 1.01, 8.003);
40 |
41 | $this->assertFalse($res1);
42 | $this->assertTrue($res2);
43 | $this->assertTrue($res3);
44 | }
45 |
46 |
47 | public function testSum() {
48 | $res1 = NumberHelper::sum(1, 3, 4, 6);
49 | $res2 = NumberHelper::sum(-1, 0.5, true, [], 4, 'hello', 231);
50 |
51 | $this->assertEquals(14, $res1);
52 | $this->assertEquals(234.5, $res2);
53 | }
54 |
55 |
56 | public function testAverage() {
57 | $res1 = NumberHelper::average(1, 3, 4, 6);
58 | $res2 = NumberHelper::average(-1, 0.5, true, [], 4, 'hello', 231);
59 |
60 | $this->assertEquals(3.5, $res1);
61 | $this->assertEquals(58.625, $res2);
62 | }
63 |
64 |
65 | public function testGeoDistance() {
66 | $lat1 = 30.0;
67 | $lng1 = 45.0;
68 | $lat2 = 40.0;
69 | $lng2 = 90.0;
70 |
71 | $res1 = NumberHelper::geoDistance($lng1, $lat1, $lng2, $lat2);
72 |
73 | $lat1 = 390.0;
74 | $lng1 = 405.0;
75 | $lat2 = -320.0;
76 | $lng2 = 90.0;
77 |
78 | $res2 = NumberHelper::geoDistance($lng1, $lat1, $lng2, $lat2);
79 |
80 | $res3 = number_format($res1, 7, '.', '');
81 | $res4 = number_format($res2, 7, '.', '');
82 |
83 | $this->assertEquals('4199598.4916152', $res3);
84 | $this->assertEquals($res3, $res4);
85 | }
86 |
87 |
88 | public function testNumberFormat() {
89 | $num1 = 123000;
90 | $num2 = 1234.56789;
91 |
92 | $res1 = NumberHelper::numberFormat($num1);
93 | $res2 = NumberHelper::numberFormat($num2, 3);
94 |
95 | $this->assertEquals('123000.00', $res1);
96 | $this->assertEquals('1234.568', $res2);
97 | }
98 |
99 |
100 | public function testNumberSub() {
101 | $num1 = '123000';
102 | $num2 = 1234.56789;
103 |
104 | $res1 = NumberHelper::numberSub($num1, 0);
105 | $res2 = NumberHelper::numberSub($num2, 3);
106 |
107 | $this->assertEquals('123000', $res1);
108 | $this->assertEquals('1234.567', $res2);
109 | }
110 |
111 |
112 | public function testRandFloat() {
113 | $tests = [
114 | [0, 1],
115 | [1, 9],
116 | [-5, 5],
117 | [-1204, 6534],
118 | ];
119 | foreach ($tests as $test) {
120 | $expected = NumberHelper::randFloat($test[0], $test[1]);
121 | $chk = NumberHelper::inRange($expected, $test[0], $test[1]);
122 | $this->assertTrue($chk);
123 | }
124 | }
125 |
126 |
127 | public function testMoney2Yuan() {
128 | $num0 = 123456789087654;
129 | $num1 = 123456789876;
130 | $num2 = 12345678908.7;
131 | $num3 = 12345678908.76;
132 | $num4 = 12345678908.765;
133 | $num5 = 12345678908.7654;
134 |
135 | try {
136 | NumberHelper::money2Yuan($num0);
137 | } catch (Throwable $e) {
138 | $this->assertTrue($e instanceof BaseException);
139 | }
140 |
141 | $res1 = NumberHelper::money2Yuan($num1);
142 | $res2 = NumberHelper::money2Yuan($num2, 1);
143 | $res3 = NumberHelper::money2Yuan($num3, 2);
144 | $res4 = NumberHelper::money2Yuan($num4, 3);
145 | $res5 = NumberHelper::money2Yuan($num5, 5);
146 | $res6 = NumberHelper::money2Yuan(0.0, 5);
147 |
148 | $this->assertEquals($res1, '壹仟贰佰叁拾肆亿伍仟陆佰柒拾捌万玖仟捌佰柒拾陆元整');
149 | $this->assertEquals($res2, '壹佰贰拾叁亿肆仟伍佰陆拾柒万捌仟玖佰零捌元柒角整');
150 | $this->assertEquals($res3, '壹佰贰拾叁亿肆仟伍佰陆拾柒万捌仟玖佰零捌元柒角陆分整');
151 | $this->assertEquals($res4, '壹佰贰拾叁亿肆仟伍佰陆拾柒万捌仟玖佰零捌元柒角陆分伍厘整');
152 | $this->assertEquals($res4, $res5);
153 | $this->assertEquals($res6, '零元整');
154 | }
155 |
156 |
157 | public function testNearLogarithm() {
158 | $res1 = NumberHelper::nearLogarithm(1000, 10);
159 | $res2 = NumberHelper::nearLogarithm(1005, 10, true);
160 | $res3 = NumberHelper::nearLogarithm(1005, 10, false);
161 |
162 | $res4 = NumberHelper::nearLogarithm(8, 2, true);
163 | $res5 = NumberHelper::nearLogarithm(8, 2, false);
164 |
165 | $res6 = NumberHelper::nearLogarithm(14, 2, true);
166 | $res7 = NumberHelper::nearLogarithm(14, 2, false);
167 |
168 | $this->assertEquals($res1, 3);
169 | $this->assertEquals($res2, 3);
170 | $this->assertEquals($res3, 4);
171 | $this->assertEquals($res4, $res5);
172 | $this->assertEquals($res6, 3);
173 | $this->assertEquals($res7, 4);
174 |
175 | try {
176 | NumberHelper::nearLogarithm(-14, 2, false);
177 | } catch (Throwable $e) {
178 | $this->assertTrue($e instanceof BaseException);
179 | }
180 | try {
181 | NumberHelper::nearLogarithm(19, -2, false);
182 | } catch (Throwable $e) {
183 | $this->assertTrue($e instanceof BaseException);
184 | }
185 | }
186 |
187 |
188 | public function testSplitNaturalNum() {
189 | $res1 = NumberHelper::splitNaturalNum(15, 2);
190 | $res2 = NumberHelper::splitNaturalNum(36, 2);
191 | $res3 = NumberHelper::splitNaturalNum(37, 2);
192 | $res4 = NumberHelper::splitNaturalNum(4, 2);
193 |
194 | $this->assertEquals(4, count($res1));
195 | $this->assertEquals(2, count($res2));
196 | $this->assertEquals(3, count($res3));
197 | $this->assertEquals(4, current($res4));
198 |
199 | try {
200 | NumberHelper::splitNaturalNum(-14, 2, false);
201 | } catch (Throwable $e) {
202 | $this->assertTrue($e instanceof BaseException);
203 | }
204 | try {
205 | NumberHelper::splitNaturalNum(19, -2, false);
206 | } catch (Throwable $e) {
207 | $this->assertTrue($e instanceof BaseException);
208 | }
209 | }
210 |
211 |
212 | }
--------------------------------------------------------------------------------
/tests/Unit/ObjectsTest.php:
--------------------------------------------------------------------------------
1 | name = 'hello';
37 | $baseObj->gender = 0;
38 |
39 | $this->assertEquals(strval($baseObj), get_class($baseObj));
40 | $this->assertEquals($baseObj::getShortName(), 'BaseCls');
41 |
42 | $arr1 = BaseCls::parseNamespacePath();
43 | $arr2 = BaseObject::parseNamespacePath($baseObj);
44 | $arr3 = BaseObject::parseNamespacePath("\Kph\Objects\BaseObject");
45 |
46 | $cls1 = StrictCls::getShortName();
47 | $cls2 = BaseObject::getShortName($baseObj);
48 | $cls3 = BaseObject::getShortName("\PHPUnit\Framework\TestCase");
49 |
50 | $nsp1 = StrictCls::getNamespaceName();
51 | $nsp2 = BaseObject::getNamespaceName($baseObj);
52 | $nsp3 = BaseObject::getNamespaceName("\PHPUnit\Framework\TestCase");
53 |
54 | $this->assertEquals(4, count($arr1));
55 | $this->assertEquals(4, count($arr2));
56 | $this->assertEquals(3, count($arr3));
57 |
58 | $this->assertEquals('StrictCls', $cls1);
59 | $this->assertEquals('BaseCls', $cls2);
60 | $this->assertEquals('TestCase', $cls3);
61 |
62 | $this->assertEquals("Kph\Tests\Objects", $nsp1);
63 | $this->assertEquals("Kph\Tests\Objects", $nsp2);
64 | $this->assertEquals("PHPUnit\Framework", $nsp3);
65 | }
66 |
67 |
68 | /**
69 | * 严格对象测试
70 | * @throws ReflectionException
71 | * @throws Throwable
72 | */
73 | public function testStrict() {
74 | $striObj = new StrictCls(['name' => 'zhang3']);
75 | $ref = $striObj->getReflectionObject();
76 | $this->assertTrue($ref instanceof ReflectionClass);
77 |
78 | // 空属性
79 | try {
80 | $striObj->get('');
81 | } catch (Exception $e) {
82 | $this->assertTrue(stripos($e->getMessage(), 'empty property') !== false);
83 | }
84 |
85 | // 访问protected属性
86 | $gender = $striObj->get('gender');
87 | $this->assertEquals($gender, 'man');
88 |
89 | $nick = $striObj->get('nick');
90 | $this->assertEquals($nick, 'boot');
91 |
92 | // 访问private属性
93 | $id = $striObj->get('id');
94 | $this->assertEquals($id, 1);
95 |
96 | // 访问不存在的属性
97 | try {
98 | $none = $striObj->get('none');
99 | } catch (Exception $e) {
100 | $this->assertTrue(stripos($e->getMessage(), 'Undefined readable property') !== false);
101 | }
102 |
103 | // 访问属性存在,但getXXX方法私有
104 | try {
105 | $no = $striObj->get('no');
106 | } catch (Exception $e) {
107 | $this->assertTrue(stripos($e->getMessage(), 'Undefined readable property') !== false);
108 | }
109 |
110 | // 设置protected属性
111 | $gender = 'woman';
112 | $striObj->set('gender', $gender);
113 | $this->assertEquals($gender, $striObj->get('gender'));
114 |
115 | $nick = 'hello';
116 | $striObj->set('nick', $nick);
117 | $this->assertEquals($nick, $striObj->get('nick'));
118 |
119 | // 设置private属性
120 | $id = 5;
121 | $striObj->set('id', $id);
122 | $this->assertEquals($id, $striObj->get('id'));
123 |
124 | // 设置不存在的属性
125 | try {
126 | $striObj->set('none', true);
127 | } catch (Exception $e) {
128 | $this->assertTrue(stripos($e->getMessage(), 'Undefined writable property') !== false);
129 | }
130 |
131 | // 设置属性存在,但getXXX方法私有
132 | try {
133 | $striObj->set('no', 2);
134 | } catch (Exception $e) {
135 | $this->assertTrue(stripos($e->getMessage(), 'Undefined writable property') !== false);
136 | }
137 |
138 | // 属性值为null
139 | $key = 'name';
140 | $this->assertEquals(true, $striObj->isset($key));
141 | $striObj->set($key, null);
142 | $this->assertEquals(true, $striObj->isset($key));
143 |
144 | // 销毁属性
145 | $striObj->unset($key);
146 | $this->assertEquals(false, $striObj->isset($key));
147 | $this->assertEquals(false, $striObj->isset(null));
148 |
149 | // json化
150 | $json1 = json_encode($striObj);
151 | $arr1 = json_decode($json1, true);
152 | $json2 = $striObj->toJson();
153 | $arr2 = $striObj->toArray();
154 |
155 | $this->assertEquals($json1, $json2);
156 | $this->assertEqualsCanonicalizing($arr1, $arr2);
157 |
158 | }
159 |
160 |
161 | /**
162 | * 数组对象测试
163 | */
164 | public function testArray() {
165 | $arrObj = new ArrayObject(['a' => 1, 'b' => 2, 'c' => 3]);
166 |
167 | $this->assertEquals($arrObj->a, 1);
168 |
169 | $arrObj->d = 4;
170 | $this->assertTrue($arrObj->offsetExists('d'));
171 |
172 | $arrObj->offsetSet('d', 5);
173 | $this->assertEquals($arrObj->offsetGet('d'), 5);
174 |
175 | $arrObj->set('e', 5);
176 | $this->assertEquals($arrObj->get('e'), 5);
177 |
178 | //json
179 | $json1 = json_encode($arrObj);
180 | $json2 = $arrObj->toJson();
181 | $this->assertEquals($json1, $json2);
182 |
183 | $count1 = $arrObj->count();
184 | $seri = $arrObj->serialize();
185 | $arrObj->unserialize($seri);
186 | $count2 = $arrObj->count();
187 | $this->assertEquals($count1, $count2);
188 |
189 | $this->assertEquals($arrObj->current(), 1);
190 | $this->assertEquals($arrObj->next(), 2);
191 |
192 | $this->assertEquals($arrObj->key(), 'b');
193 | $this->assertTrue($arrObj->valid());
194 |
195 | $arrObj->rewind();
196 | $this->assertEquals($arrObj->key(), 'a');
197 |
198 | $arr = $arrObj->toArray();
199 | $this->assertEquals($arrObj->count(), count($arr));
200 |
201 | $idx = $arrObj->search(3);
202 | $idx2 = $arrObj->indexOf(3);
203 | $this->assertEquals($idx, $idx2);
204 |
205 | $idx3 = $arrObj->lastIndexOf(5);
206 | $this->assertEquals($idx3, 'e');
207 |
208 | $keys = $arrObj->keys(5);
209 | $this->assertEquals($keys->count(), 2);
210 |
211 | $this->assertTrue($arrObj->delete('e'));
212 |
213 | $arrObj->remove(5);
214 | $arrObj->offsetUnset('c');
215 | $this->assertFalse($arrObj->exists('e'));
216 | $this->assertFalse($arrObj->exists('c'));
217 | $this->assertFalse($arrObj->contains(5));
218 |
219 | $str = $arrObj->join(',');
220 | $this->assertFalse(empty($str));
221 |
222 | $arrObj->clear();
223 | $this->assertTrue($arrObj->isEmpty());
224 |
225 | $arrObj->insert(0, 2);
226 | $arrObj->insert(2, 6);
227 | $arrObj->append(3);
228 | $arrObj->prepend(1);
229 |
230 | $sum = $arrObj->sum();
231 | $pro = $arrObj->product();
232 | $sum2 = $arrObj->reduce(function ($carry, $item) {
233 | $carry += $item;
234 | return $carry;
235 | });
236 | $this->assertEquals($sum, $pro);
237 | $this->assertEquals($sum, $sum2);
238 |
239 | $obj2 = $arrObj->slice(0, 2);
240 | $this->assertEquals($obj2->count(), 2);
241 |
242 | $obj2->pop();
243 | $obj2->shift();
244 | $this->assertEquals($obj2->count(), 0);
245 |
246 | $item = $arrObj->rand();
247 | $this->assertTrue($arrObj->contains($item));
248 |
249 | $arrObj->each(function (&$val, $key) {
250 | $val = pow($val, $key);
251 | });
252 | $this->assertEquals($arrObj->sum(), 12);
253 |
254 | $obj3 = $arrObj->map(function ($val) {
255 | return $val * 2;
256 | });
257 | $this->assertEquals($obj3->sum(), 24);
258 |
259 | $values = $arrObj->values();
260 | $keys = $arrObj->keys();
261 | $this->assertEquals($values->count(), $keys->count());
262 |
263 | $arrObj->prepend(2);
264 | $arrObj->prepend(3);
265 | $arrObj->prepend(9);
266 |
267 | $obj4 = $arrObj->unique();
268 | $this->assertTrue($obj4->count() < $arrObj->count());
269 |
270 | $obj5 = $arrObj->multiple();
271 | $this->assertEquals($obj5->count(), 2);
272 |
273 | $arrObj->sort();
274 | $this->assertEquals($arrObj->current(), 1);
275 |
276 | $arrObj->reverse();
277 | $this->assertEquals($arrObj->current(), 9);
278 |
279 | $arrObj->shuffle();
280 | $obj6 = $arrObj->chunk(3);
281 | $this->assertEquals($obj6->count(), 2);
282 |
283 | $obj7 = $obj4->flip();
284 | $this->assertEquals($obj7->current(), 0);
285 |
286 | $obj8 = $arrObj->filter(function ($val) {
287 | return $val > 4;
288 | });
289 | $this->assertEquals($obj8->count(), 2);
290 |
291 | $arrObj->clear();
292 | $arrObj->append(['name' => 'zhang3', 'age' => '20',]);
293 | $arrObj->append(['name' => 'li4', 'age' => '22',]);
294 | $arrObj->append(['name' => 'zhao5', 'age' => '33',]);
295 | $arrObj->append(['name' => 'wang6', 'age' => '45',]);
296 | $names = $arrObj->column('name');
297 | $this->assertEquals($names->count(), 4);
298 |
299 | }
300 |
301 |
302 | public function testGetClassMethods() {
303 | $res1 = BaseObject::getClassMethods(BaseCls::class);
304 | $res2 = BaseObject::getClassMethods(BaseCls::class, ReflectionMethod::IS_STATIC);
305 | $res3 = BaseObject::getClassMethods(BaseCls::class, ReflectionMethod::IS_PUBLIC, false);
306 | $dif1 = array_diff($res1, $res2);
307 | $chk1 = ValidateHelper::isEqualArray($res3, ['time', '__call']);
308 | $chk2 = ValidateHelper::isEqualArray($dif1, ['time', '__call', '__toString']);
309 | $this->assertTrue($chk1);
310 | $this->assertTrue($chk2);
311 |
312 | $res4 = BaseObject::getClassMethods(StrictCls::class);
313 | $res5 = BaseObject::getClassMethods(StrictCls::class, ReflectionMethod::IS_PROTECTED);
314 | $res6 = BaseObject::getClassMethods(StrictCls::class, ReflectionMethod::IS_PUBLIC, false);
315 | $dif2 = array_diff($res4, $res5);
316 | $chk3 = ValidateHelper::isEqualArray($res6, ['world']);
317 | $this->assertTrue($chk3);
318 | $this->assertNotEmpty($dif2);
319 |
320 | }
321 |
322 |
323 | }
--------------------------------------------------------------------------------
/tests/Unit/OsHelperTest.php:
--------------------------------------------------------------------------------
1 | '/var/www',
25 | 'REMOTE_ADDR' => '172.17.0.1',
26 | 'REMOTE_PORT' => '51186',
27 | 'SERVER_SOFTWARE' => 'PHP 7.4.3 Development Server',
28 | 'SERVER_PROTOCOL' => 'HTTP/1.1',
29 | 'SERVER_NAME' => '0.0.0.0',
30 | 'SERVER_PORT' => '8000',
31 | 'REQUEST_URI' => '/index.php?name=hello&age=20&from=world',
32 | 'REQUEST_METHOD' => 'GET',
33 | 'SCRIPT_NAME' => '/index.php',
34 | 'SCRIPT_FILENAME' => '/var/www/index.php',
35 | 'PHP_SELF' => '/index.php',
36 | 'QUERY_STRING' => 'name=hello&age=20&from=world',
37 | 'HTTP_X_REAL_IP' => '192.168.56.1',
38 | 'HTTP_X_FORWARDED_FOR' => '192.168.56.1',
39 | 'HTTP_X_REAL_PORT' => '80',
40 | 'HTTP_X_FORWARDED_PROTO' => 'http',
41 | 'HTTP_HOST' => 'www.test.loc',
42 | 'HTTP_USER_AGENT' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0',
43 | 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
44 | 'HTTP_ACCEPT_LANGUAGE' => 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
45 | 'HTTP_ACCEPT_ENCODING' => 'gzip, deflate',
46 | 'HTTP_UPGRADE_INSECURE_REQUESTS' => '1',
47 | 'REQUEST_TIME_FLOAT' => 1582623381.699998,
48 | 'REQUEST_TIME' => 1582623381,
49 | ];
50 |
51 |
52 | public function testGetOS() {
53 | $res = OsHelper::getOS();
54 | $this->assertNotEmpty($res);
55 | }
56 |
57 |
58 | public function testIsWindowsLinuxMac() {
59 | $res1 = OsHelper::isWindows();
60 | $res2 = OsHelper::isLinux();
61 | $res3 = OsHelper::isMac();
62 |
63 | $this->assertTrue($res1 || $res2);
64 | $this->assertFalse($res3);
65 | }
66 |
67 |
68 | public function testGetPhpPath() {
69 | $res = OsHelper::getPhpPath();
70 | $this->assertNotEmpty($res);
71 | }
72 |
73 |
74 | public function testIsPortOpen() {
75 | $res1 = OsHelper::isPortOpen('localhost', 8899);
76 | $res2 = OsHelper::isPortOpen('baidu.com', 80);
77 |
78 | $this->assertFalse($res1);
79 | $this->assertTrue($res2);
80 | }
81 |
82 |
83 | public function testIsWritable() {
84 | $res1 = OsHelper::isWritable(TESTDIR . 'tmp');
85 | $res2 = OsHelper::isWritable('/root/tmp/hehe');
86 |
87 | $this->assertTrue($res1);
88 | $this->assertFalse($res2);
89 | }
90 |
91 |
92 | public function testGetBrowser() {
93 | $agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36';
94 | $res = OsHelper::getBrowser($agent);
95 |
96 | $this->assertGreaterThan(1, stripos($res['name'], 'Chrome'));
97 | $this->assertNotEmpty($res['platform']);
98 |
99 | $agents = ValidateHelperTest::$userAgents;
100 | foreach ($agents as $agent) {
101 | $res = OsHelper::getBrowser($agent);
102 | $this->assertNotEmpty($res['name']);
103 | }
104 |
105 | $res = OsHelper::getBrowser();
106 | $this->assertEquals(Consts::UNKNOWN, $res['name']);
107 | }
108 |
109 |
110 | public function testGetClientOS() {
111 | $agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36';
112 | $res1 = OsHelper::getClientOS($agent);
113 | $res2 = OsHelper::getClientOS('');
114 | $this->assertEquals('Windows', $res1);
115 | $this->assertEquals(Consts::UNKNOWN, $res2);
116 |
117 | $agents = ValidateHelperTest::$userAgents;
118 | foreach ($agents as $agent) {
119 | $res = OsHelper::getClientOS($agent);
120 | $this->assertNotEmpty($res);
121 | }
122 | }
123 |
124 |
125 | public function testGetClientIp() {
126 | $server = self::$server;
127 | $res = OsHelper::getClientIp($server);
128 | $this->assertEquals('192.168.56.1', $res);
129 |
130 | $server['HTTP_X_FORWARDED_FOR'] = '220.181.38.148';
131 | $res = OsHelper::getClientIp($server);
132 | $this->assertEquals('220.181.38.148', $res);
133 |
134 | unset($server['HTTP_X_FORWARDED_FOR']);
135 | $res = OsHelper::getClientIp($server);
136 | $this->assertEquals('172.17.0.1', $res);
137 |
138 | $res = OsHelper::getClientIp();
139 | $this->assertEquals('0.0.0.0', $res);
140 | }
141 |
142 |
143 | public function testGetServerIP() {
144 | $server = self::$server;
145 | $res = OsHelper::getServerIP($server);
146 | $this->assertNotEmpty($res);
147 | $this->assertNotEquals('0.0.0.0', $res);
148 |
149 | $res = OsHelper::getServerIP();
150 | $this->assertNotEmpty($res);
151 | $this->assertNotEquals('0.0.0.0', $res);
152 |
153 | putenv("SERVER_ADDR='192.168.1.1'");
154 | $res = OsHelper::getServerIP([]);
155 | $this->assertNotEquals('192.168.1.1', $res);
156 | }
157 |
158 |
159 | public function testIp2UnsignedInt() {
160 | $ip1 = '172.17.0.1';
161 | $ip2 = '192.168.56.1';
162 | $ip3 = 'hello';
163 | $ip4 = '200.117.248.17';
164 |
165 | $res1 = OsHelper::ip2UnsignedInt($ip1);
166 | $res2 = OsHelper::ip2UnsignedInt($ip2);
167 | $res3 = OsHelper::ip2UnsignedInt($ip3);
168 | $res4 = OsHelper::ip2UnsignedInt($ip4);
169 | $res5 = OsHelper::ip2UnsignedInt('');
170 |
171 | $this->assertGreaterThan(1, $res1);
172 | $this->assertGreaterThan(1, $res2);
173 | $this->assertEquals(0, $res3);
174 | $this->assertGreaterThan(1, $res4);
175 | $this->assertEquals(0, $res5);
176 | }
177 |
178 |
179 | public function testGetRemoteImageSize() {
180 | $url = 'https://www.baidu.com/img/bd_logo1.png';
181 |
182 | $res1 = OsHelper::getRemoteImageSize($url . '?a=1', 'hello', false, 5, 256);
183 | $res2 = OsHelper::getRemoteImageSize($url . '?a=2', 'curl', true, 5, 256);
184 | $res3 = OsHelper::getRemoteImageSize('http://test.loc/img/hello.jpg');
185 |
186 | $this->assertNotEmpty($res1);
187 | $this->assertEquals($res1['width'], $res2['width']);
188 | $this->assertEquals($res1['height'], $res2['height']);
189 | $this->assertEquals(0, $res1['size']);
190 | $this->assertGreaterThan(1, $res2['size']);
191 | $this->assertEmpty($res3);
192 |
193 | OsHelper::getRemoteImageSize('https://raw.githubusercontent.com/kakuilan/kgo/master/testdata/gopher10th-large.jpg', 'curl', true, 1, 24);
194 | }
195 |
196 |
197 | public function testCurlDownload() {
198 | $des1 = $backupDir1 = TESTDIR . 'tmp/download.txt';
199 | $des2 = $backupDir1 = TESTDIR . 'tmp/hello/download.txt';
200 |
201 | $res1 = OsHelper::curlDownload('http://test.loc/hello', '', [], false);
202 | $res2 = OsHelper::curlDownload('https://www.baidu.com/', '', ['connect_timeout' => 5, 'timeout' => 5], true);
203 | $res3 = OsHelper::curlDownload('hello world');
204 |
205 | $res4 = OsHelper::curlDownload('https://www.baidu.com/', $des1, ['connect_timeout' => 5, 'timeout' => 5], false);
206 | $res5 = OsHelper::curlDownload('https://www.baidu.com/', $des1, ['connect_timeout' => 5, 'timeout' => 5], true);
207 | $res6 = OsHelper::curlDownload('https://www.baidu.com/', $des2, ['connect_timeout' => 5, 'timeout' => 5], false);
208 |
209 | $this->assertFalse($res1);
210 | $this->assertNotEmpty($res2);
211 | $this->assertFalse($res3);
212 | $this->assertTrue($res4);
213 | $this->assertNotEmpty($res5);
214 | $this->assertFalse($res6);
215 | }
216 |
217 |
218 | public function testIsCliMode() {
219 | $chk = OsHelper::isCliMode();
220 | $this->assertTrue($chk);
221 | }
222 |
223 |
224 | public function testRunCommand() {
225 | $dir = TESTDIR;
226 | if (OsHelper::isWindows()) {
227 | $dir = str_replace('/', '\\', $dir);
228 | $command = "dir {$dir}";
229 | } else {
230 | $command = "ls -l {$dir}";
231 | }
232 |
233 | $res0 = OsHelper::runCommand("");
234 | $res1 = OsHelper::runCommand($command);
235 |
236 | $this->assertEmpty($res0);
237 | $this->assertNotEmpty($res1);
238 | }
239 |
240 |
241 | public function testIsAjax() {
242 | $header1 = [
243 | 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36',
244 | 'X-Requested-With' => 'XMLHttpRequest',
245 | ];
246 | $header2 = [
247 | 'Accept-Language' => 'zh-CN,zh;q=0.9,en;q=0.8',
248 | 'Connection' => 'keep-alive',
249 | ];
250 |
251 | $res1 = OsHelper::isAjax();
252 | $res2 = OsHelper::isAjax($header1);
253 | $res3 = OsHelper::isAjax($header2);
254 |
255 | $this->assertFalse($res1);
256 | $this->assertTrue($res2);
257 | $this->assertFalse($res3);
258 | }
259 |
260 |
261 | public function testIsSsl() {
262 | $server1 = [
263 | 'HTTP_HOST' => 'test.loc',
264 | 'SERVER_NAME' => 'test.loc',
265 | 'SERVER_PORT' => '80',
266 | 'SERVER_ADDR' => '127.0.0.1',
267 | 'REQUEST_SCHEME' => 'http',
268 | 'SERVER_PROTOCOL' => 'HTTP/1.1',
269 | ];
270 | $server2 = [
271 | 'HTTP_HOST' => 'test.com',
272 | 'SERVER_NAME' => 'test.com',
273 | 'SERVER_PORT' => '443',
274 | 'SERVER_ADDR' => '127.0.0.1',
275 | 'HTTPS' => 'on',
276 | 'REQUEST_SCHEME' => 'https',
277 | 'SERVER_PROTOCOL' => 'HTTP/2.0',
278 | ];
279 |
280 | $res1 = OsHelper::isSsl($server1);
281 | $res2 = OsHelper::isSsl($server2);
282 |
283 | $this->assertFalse($res1);
284 | $this->assertTrue($res2);
285 | }
286 |
287 |
288 | public function testRemoteFileExists() {
289 | $url1 = 'https://www.baidu.com';
290 | $url2 = 'https://www.baidu.com/img/no.gif';
291 |
292 | $res1 = OsHelper::remoteFileExists($url1);
293 | $res2 = OsHelper::remoteFileExists($url2, ['timeout' => 10, 'connect_timeout' => 2]);
294 |
295 | $this->assertTrue($res1);
296 | $this->assertFalse($res2);
297 | }
298 |
299 |
300 | }
--------------------------------------------------------------------------------
/tests/Unit/ServicesTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($servName2, 'BaseObject');
31 | $this->assertTrue(BaseServ::hasFinalInstance());
32 | BaseServ::destroyFinalInstance();
33 | $this->assertFalse(BaseServ::hasFinalInstance());
34 |
35 | // 获取当前类的实例化
36 | $serv3 = BaseServ::getSelfInstance();
37 | $servName3 = $serv3::getShortName();
38 | $this->assertEquals($servName3, 'BaseServ');
39 | $this->assertTrue(BaseServ::hasSelfInstance());
40 | BaseServ::destroySelfInstance();
41 | $this->assertFalse(BaseServ::hasSelfInstance());
42 |
43 | $serv->setErrorInfo(123, '找不到资源');
44 | $errArr = $serv->getErrorInfo();
45 | $errno = $serv->getErrno();
46 | $error = $serv->getError();
47 | $this->assertEquals($errArr['errno'], $errno);
48 | $this->assertEquals($errArr['error'], $error);
49 |
50 | }
51 |
52 |
53 | }
--------------------------------------------------------------------------------
/tests/Unit/UrlHelperTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($url, $res2);
32 | $this->assertEquals('©℗', $res3);
33 | }
34 |
35 |
36 | public function testBuildUriParams() {
37 | $arr = [
38 | 'name' => 'li4',
39 | 'age' => 28,
40 | 'has' => [
41 | 0 => 'apple',
42 | 1 => 'watermelon',
43 | 2 => 'banana',
44 | 'favorite' => 'litchi',
45 | ],
46 | 'from' => 'xiguan',
47 | 'to' => 'baoan',
48 | ];
49 |
50 | $res1 = UrlHelper::buildUriParams($arr);
51 | $res2 = UrlHelper::buildUriParams($arr, ['from', 'to']);
52 | $res3 = UrlHelper::buildUriParams($arr, ['from', 'to'], ['futian', 'dogu']);
53 |
54 | $this->assertEquals('?', substr($res1, 0, 1));
55 | $this->assertFalse(stripos($res2, 'from'));
56 | $this->assertTrue(stripos($res3, 'from') !== false);
57 | }
58 |
59 |
60 | public function testFormatUrl() {
61 | $url = 'www.test.loc//abc\\hello/\kit\/name=zang&age=11';
62 | $res = UrlHelper::formatUrl($url);
63 |
64 | $this->assertEquals('http://www.test.loc/abc/hello/kit/name=zang&age=11', $res);
65 | }
66 |
67 |
68 | public function testCheckUrlExists() {
69 | $res1 = UrlHelper::checkUrlExists('');
70 | $res2 = UrlHelper::checkUrlExists('hello world');
71 | $res3 = UrlHelper::checkUrlExists('https://www.baidu.com/');
72 |
73 | $this->assertFalse($res1);
74 | $this->assertFalse($res2);
75 | $this->assertTrue($res3);
76 | }
77 |
78 |
79 | public function testUrl2Link() {
80 | $res1 = UrlHelper::url2Link('');
81 | $res2 = UrlHelper::url2Link('http://google.com');
82 | $res3 = UrlHelper::url2Link('ftp://192.168.1.2/abc.pdf', ['ftp']);
83 | $res4 = UrlHelper::url2Link('test@qq.com', ['mail']);
84 |
85 | $this->assertEmpty($res1);
86 | $this->assertTrue(stripos($res2, 'href') !== false);
87 | $this->assertTrue(stripos($res3, 'href') !== false);
88 | $this->assertTrue(stripos($res4, 'href') !== false);
89 | }
90 |
91 |
92 | public function testGetDomainUrlUri() {
93 | $server = OsHelperTest::$server;
94 | $url = 'http://www.test.loc/index.php?name=hello&age=20&from=world';
95 | $res1 = UrlHelper::getDomain($url, false, $server);
96 | $res2 = UrlHelper::getDomain($url, true, $server);
97 |
98 | $this->assertEquals('www.test.loc', $res1);
99 | $this->assertEquals('test.loc', $res2);
100 |
101 | $res3 = UrlHelper::getUrl($server);
102 | $this->assertEquals($url, $res3);
103 |
104 | $res4 = UrlHelper::getUri($server);
105 | unset($server['REQUEST_URI']);
106 | $res5 = UrlHelper::getUri($server);
107 | $this->assertEquals($res4, $res5);
108 |
109 | $res6 = UrlHelper::getDomain('', true, $server);
110 | $this->assertEquals('test.loc', $res6);
111 |
112 | $res7 = UrlHelper::getUrl();
113 | $chk = ValidateHelper::isUrl($res7);
114 | $this->assertFalse($chk);
115 |
116 | $res8 = UrlHelper::getUri();
117 | $this->assertNotEmpty($res8);
118 |
119 | $res9 = UrlHelper::getDomain('');
120 | $this->assertEmpty($res9);
121 | }
122 |
123 |
124 | public function testGetSiteUrl() {
125 | $server = OsHelperTest::$server;
126 | $str = 'hello world!';
127 | $url1 = 'http://www.test.loc/index.php?name=hello&age=20&from=world';
128 | $url2 = 'rpc.test.com:8899/hello';
129 |
130 | $res1 = UrlHelper::getSiteUrl($str);
131 | $this->assertEmpty($res1);
132 |
133 | $res2 = UrlHelper::getSiteUrl('', $server);
134 | $this->assertNotEmpty($res2);
135 |
136 | $res3 = UrlHelper::getSiteUrl($url1);
137 | $this->assertNotEmpty($res3);
138 |
139 | $res4 = UrlHelper::getSiteUrl($url2);
140 | $this->assertNotEmpty($res4);
141 | }
142 |
143 |
144 | }
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/tests/data/php_elephant.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kakuilan/php-helper/f34ebbb5082b20e87201ca2d0c5ea808c43616c6/tests/data/php_elephant.png
--------------------------------------------------------------------------------
/tests/data/png.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kakuilan/php-helper/f34ebbb5082b20e87201ca2d0c5ea808c43616c6/tests/data/png.webp
--------------------------------------------------------------------------------
/tests/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
15 |
16 | ./Unit
17 |
18 |
19 | ./Feature
20 |
21 |
22 |
23 |
24 |
25 | ../src/
26 |
27 |
28 |
--------------------------------------------------------------------------------
/tests/tmp/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kakuilan/php-helper/f34ebbb5082b20e87201ca2d0c5ea808c43616c6/tests/tmp/.gitkeep
--------------------------------------------------------------------------------