diff --git a/.circleci/config.yml b/.circleci/config.yml index 61bc287..8bba5f3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,47 +4,60 @@ # version: 2 jobs: - build: - branches: - # only: - # - master # list of branches to build - # - develop - ignore: - - feature/* + test-php-common: &common-test docker: - # specify the version you desire here - image: circleci/php:7.1-browsers - - # Specify service dependencies here if necessary - # CircleCI maintains a library of pre-built images - # documented at https://circleci.com/docs/2.0/circleci-images/ - # - image: circleci/mysql:9.4 - working_directory: ~/repo - steps: - - checkout - - - run: - name: startup install - command: phpize && ./configure && make && sudo make install - - - run: - name: composer install - command: composer install - - - run: - name: run test - command: ./vendor/bin/kahlan - - - run: - name: cp docker-php-ext-start.ini - command: sudo cp .circleci/docker-php-ext-startup.ini /usr/local/etc/php/conf.d/ + - run: + name: php version + command: php -v + - checkout + - run: + name: composer install + command: composer install --no-interaction --prefer-source + - run: + name: run test + command: ./vendor/bin/kahlan + - run: + name: upload coverage + command: bash <(curl -s https://codecov.io/bash) + - run: + name: startup install + command: phpize && ./configure && make && sudo make install + - run: + name: enable startup + command: sudo cp .circleci/docker-php-ext-startup.ini /usr/local/etc/php/conf.d/ + - run: + name: run test + command: ./vendor/bin/kahlan + test-php74: + <<: *common-test + docker: + - image: circleci/php:7.4-browsers + test-php73: + <<: *common-test + docker: + - image: circleci/php:7.3-browsers + test-php72: + <<: *common-test + docker: + - image: circleci/php:7.2-browsers + test-php71: + <<: *common-test + docker: + - image: circleci/php:7.1-browsers + test-php70: + <<: *common-test + docker: + - image: circleci/php:7.0-browsers +workflows: + version: 2 + build: + jobs: + - test-php70 + - test-php71 + - test-php72 + - test-php73 + - test-php74 - - run: - name: enable startup - command: sudo docker-php-ext-enable startup - - - run: - name: run test - command: ./vendor/bin/kahlan diff --git a/.clang b/.clang new file mode 100644 index 0000000..cae8aca --- /dev/null +++ b/.clang @@ -0,0 +1,6 @@ +-I/home/minbaby-debain/.phpbrew/php/php-7.1.23/include/php +-I/home/minbaby-debain/.phpbrew/php/php-7.1.23/include/php/main +-I/home/minbaby-debain/.phpbrew/php/php-7.1.23/include/php/TSRM +-I/home/minbaby-debain/.phpbrew/php/php-7.1.23/include/php/Zend +-I/home/minbaby-debain/.phpbrew/php/php-7.1.23/include/php/ext +-I/home/minbaby-debain/.phpbrew/php/php-7.1.23/include/php/ext/date/lib \ No newline at end of file diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..1f8fc31 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,50 @@ +--- +kind: pipeline +type: docker +name: php-ext-startup test + +steps: + - name: install vendor + image: composer:1 + commands: + - php -v + - composer install -vvv --profile --no-interaction + - name: build-ext(7.1) + image: php:7.1-cli + commands: + - php -v + - ./vendor/bin/kahlan + - phpize && ./configure && make && make install + - cp .circleci/docker-php-ext-startup.ini /usr/local/etc/php/conf.d/ + - ./vendor/bin/kahlan + - name: build-ext(7.2) + image: php:7.2-cli + commands: + - php -v + - ./vendor/bin/kahlan + - phpize && ./configure && make && make install + - cp .circleci/docker-php-ext-startup.ini /usr/local/etc/php/conf.d/ + - ./vendor/bin/kahlan + - name: build-ext(7.3) + image: php:7.3-cli + commands: + - php -v + - ./vendor/bin/kahlan + - phpize && ./configure && make && make install + - cp .circleci/docker-php-ext-startup.ini /usr/local/etc/php/conf.d/ + - ./vendor/bin/kahlan + # - name: build-ext(7.4) + # image: php:7.4-cli + # commands: + # - php -v + # - ./vendor/bin/kahlan + # - phpize && ./configure && make && make install + # - cp .circleci/docker-php-ext-startup.ini /usr/local/etc/php/conf.d/ + # - ./vendor/bin/kahlan + - name: send-wechat + image: erguotou/drone-instant-access + settings: + channel: + from_secret: CHANNEL + head: "[${DRONE_REPO}] Build Status" + body: "[${DRONE_REPO}] ${DRONE_BUILD_STATUS} at ${DRONE_BRANCH} branch" diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1102fc0 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,25 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + +# Matches multiple files with brace expansion notation +# Set default charset +[*.{php,c,h}] +charset = utf-8 +indent_size = 4 +indent_style = space + +# Tab indentation (no size specified) +[Makefile] +indent_style = tab + +# Matches the exact files either package.json or .travis.yml +[*.{json,yml}] +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 391d310..740c89a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .deps *.lo *.la +*.so .libs acinclude.m4 aclocal.m4 @@ -14,6 +15,7 @@ config.nice config.status config.sub configure +configure.ac configure.in include install-sh @@ -35,7 +37,7 @@ tests/*/*.log tests/*/*.sh # minbaby -.vscode +# .vscode tags GRTAGS GTAGS @@ -43,4 +45,16 @@ GPATH /vendor/ *.loT -/nbproject/private/ \ No newline at end of file +/core +/.gdb_history +peda-session-php.txt +/coverage.xml +/.vscode/ipch + +#clion +/.idea/ +CMakeCache.txt +/CMakeFiles/ +/cmake-build-debug/ +cmake_install.cmake +php_ext_startup.cbp diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..143dcab --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,29 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**", + "~/.phpbrew/build/php-7.2.34/**" + ], + "defines": [], + "compilerPath": "/usr/bin/gcc", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "clang-x64" + }, + { + "name": "Mac", + "includePath": [ + "${workspaceFolder}/**", + "~/.phpbrew/build/php-7.2.34/**" + ], + "defines": [], + "compilerPath": "/usr/bin/gcc", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "clang-x64" + } + ], + "version": 4 +} diff --git a/.vscode/keybindings.json b/.vscode/keybindings.json new file mode 100644 index 0000000..55f5301 --- /dev/null +++ b/.vscode/keybindings.json @@ -0,0 +1,13 @@ +// Place your key bindings in this file to overwrite the defaults +[ + { + "key": "ctrl+k ctrl+b", + "command": "workbench.action.tasks.runTask", + "args": "run build" + }, + { + "key": "ctrl+k ctrl+t", + "command": "workbench.action.tasks.runTask", + "args": "run test" + } +] diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a7c201e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,33 @@ +{ + "files.associations": { + "*.inc": "php", + "*.h": "c", + "__locale": "c", + "__string": "c", + "string": "c", + "string_view": "c", + "array": "c", + "initializer_list": "c", + "utility": "c", + "sstream": "c", + "istream": "c", + "ostream": "c", + "functional": "c", + "tuple": "c", + "type_traits": "c", + "random": "c" + }, + "editor.mouseWheelZoom": false, + "files.autoSave": "onFocusChange", + "editor.fontSize": 14, + "editor.renderWhitespace": "boundary", + "editor.cursorStyle": "line-thin", + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.DS_Store": true + }, + "editor.minimap.enabled": true +} diff --git a/.vscode/snnipt.code-snippets b/.vscode/snnipt.code-snippets new file mode 100644 index 0000000..96b984a --- /dev/null +++ b/.vscode/snnipt.code-snippets @@ -0,0 +1,70 @@ +{ + // Place your global snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and + // description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope + // is left empty or omitted, the snippet gets applied to all languages. The prefix is what is + // used to trigger the snippet and the body will be expanded and inserted. Possible variables are: + // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. + // Placeholders with the same ids are connected. + // Example: + // "Print to console": { + // "scope": "javascript,typescript", + // "prefix": "log", + // "body": [ + // "console.log('$1');", + // "$2" + // ], + // "description": "Log output to console" + // } + "itf": { + "prefix": "ift", + "scope": "php", + "body": [ + "it('test $1', function () {", + " $0", + "});" + ], + "description": "it function scope" + }, + "cuf": { + "prefix": "cuf", + "scope": "c,h", + "body": [ + "zval func = {};", + "ZVAL_STRING(&func, \"$1\");", + "call_user_function(NULL, NULL, &func, ${2:return_value}, 0, NULL);" + ], + "description": "args" + }, + "cufa": { + "prefix": "cufa", + "scope": "c,h", + "body": [ + "zval ${1:func}, args[] = {", + " $4", + "};", + "ZVAL_STRING(&${1:func}, \"$0\");", + "call_user_function(NULL, NULL, &${1:func}, ${3:return_value}, $2, args);" + ], + "description": "args" + }, + "argparse": { + "prefix": "argparse", + "scope": "c,h", + "body": [ + "ZEND_PARSE_PARAMETERS_START($1, $2)", + " Z_PARAM_$0", + "ZEND_PARSE_PARAMETERS_END();" + ], + "description": "args" + }, + "argdefine": { + "prefix": "argdefine", + "scope": "c,h", + "body": [ + "ZEND_BEGIN_ARG_INFO($1, $2)", + " ZEND_ARG_INFO(0, $0)", + "ZEND_END_ARG_INFO();" + ], + "description": "args" + } +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..b8541c1 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,48 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "run build", + "type": "shell", + "command": "./scripts/build.sh", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$gcc" + ], + "presentation": { + "echo": true, + "reveal": "silent", + "focus": false, + "panel": "shared", + "showReuseMessage": true, + "clear": true + } + }, + { + "label": "run test", + "type": "shell", + "command": "./scripts/test.sh", + "group": { + "kind": "test", + "isDefault": true + }, + "presentation": { + "echo": true, + "reveal": "silent", + "focus": true, + "panel": "shared", + "showReuseMessage": true, + "clear": true + } + }, + { + "label": "run build && run test", + "dependsOn":["run build", "run"] + } + ] +} diff --git a/Makefile.frag b/Makefile.frag new file mode 100644 index 0000000..4da25ab --- /dev/null +++ b/Makefile.frag @@ -0,0 +1,29 @@ +ext-subtree: + ./scripts/subtree.sh + +ext-fetch: + ./scripts/fetch.php + +ext-prepare: + echo "prepare..." + phpize && ./configure --enable-debug + +ext-clean: + echo "clean..." + make clean && phpize --clean + +ext-build: + ./scripts/build.sh + +ext-test: + # echo "disable ext startup.so" + phpbrew ext disable startup + MINBABY_TEST_EXT=0 php ./vendor/bin/kahlan + +ext-test-ext: + # echo "enable ext start.so" + phpbrew ext enable startup + MINBABY_TEST_EXT=1 php ./vendor/bin/kahlan + +ext-first:ext-prepare ext-build ext-test ext-test-ext + @echo Done diff --git a/README.MD b/README.MD index a45115e..d29874f 100644 --- a/README.MD +++ b/README.MD @@ -1,8 +1,8 @@ # 学习 php 扩展 -[TOC] - [![CircleCI](https://circleci.com/gh/minbaby/php-ext-startup/tree/master.svg?style=svg)](https://circleci.com/gh/minbaby/php-ext-startup/tree/master) +[![xcxc](https://img.shields.io/github/license/minbaby/php-ext-startup.svg)](https://github.com/minbaby/php-ext-startup/blob/master/README.MD) +[![codecov](https://codecov.io/gh/minbaby/php-ext-startup/branch/master/graph/badge.svg)](https://codecov.io/gh/minbaby/php-ext-startup) ## 基本思路 @@ -11,13 +11,14 @@ 3. 再用 ext 实现 4. 跑测试 - ## 测试思路 利用 php [自动加载](http://www.php.net/manual/zh/language.oop5.autoload.php)的逻辑。 -1. 默认禁用扩展执行一遍测试,这个时候执行的是 PHP 逻辑 -2. 启用扩展再执行一次扩展, 这个时候执行的是 EXT 逻辑 +1. ~~默认禁用扩展执行一遍测试,这个时候执行的是 PHP 逻辑~~ +2. ~~启用扩展再执行一次扩展, 这个时候执行的是 EXT 逻辑 (这个地方有个问题,如果 ext 扩展不存在,那么就会降级使用 php 代码)~~ + +使用函数动态生成对应的带有命名空间的类名,原因是之前的思路存在漏洞,因为自动加载的存在,导致如果 ext 中不存在,就会自动降级为 php 代码。 ## 学习方式 @@ -27,7 +28,59 @@ 2. 查看 `src/Minbaby/` 目录下的 php 实现 3. 查看 `src/ext` 目录下的 c 实现 -## 其他 +## 常用命令 + +1. 测试所有 + +```bash +composer run test +# or +make ext-first +``` + +2. 测试 php 部分代码 + +```bash +composer run test-php +# or +make ext-test +``` + +3. 测试 ext 部分代码 + +```bash +composer run test-ext +# or +make ext-test-ext +``` + +4. 其他命令 + +```bash +make ext-clean # clean code + +make ext-fetch # fetch configure ext code from github + +make ext-prepare # phpize & configure + +make ext-buidl +``` + + +## TODO + +- [ ] [stringy](https://github.com/danielstjules/Stringy) + +## 说明 + +- 本项目只是学习实践使用 +- 使用 `@` 抑制符不是一个好习惯,尽量不要使用。 +- 依赖 `mb_string` 扩展 + +## 已知问题 + +- 0.0 很多 +- memory leak (可能使用改方案来排查 https://github.com/arnaud-lb/php-memory-profiler) ### skip ci @@ -37,4 +90,4 @@ ### 参考 - [phpbook](https://github.com/walu/phpbook) -- [Kahlan](https://kahlan.github.io/docs/index.html) \ No newline at end of file +- [Kahlan](https://kahlan.github.io/docs/index.html) diff --git a/composer.json b/composer.json index 356b160..0b02e1e 100644 --- a/composer.json +++ b/composer.json @@ -2,6 +2,7 @@ "name": "minbaby/php-ext-startup", "type": "project", "license": "MIT", + "description": "", "authors": [ { "name": "minbaby.zhang", @@ -9,19 +10,62 @@ } ], "require": { - + "php": "^7.0", + "ext-json": "*" }, "autoload": { "psr-4": { - "Minbaby\\Ext\\": "src/Minbaby/" + "Minbaby\\Startup\\Php\\": "src/Minbaby/Php" } }, "autoload-dev": { "psr-4": { - "Minbaby\\Ext\\Spec\\": "spec/" - } + "Minbaby\\Startup\\Spec\\": "spec/" + }, + "files": [ + "spec/const.php", + "spec/functions.php" + ] }, "require-dev": { "kahlan/kahlan": "^4.3" + }, + "config": { + "preferred-install": "dist", + "sort-packages": true, + "optimize-autoloader": true + }, + "repositories": { + "packagist": { + "type": "composer", + "url": "https://mirrors.aliyun.com/composer/" + } + }, + "scripts": { + "init-dev": [ + "Composer\\Config::disableProcessTimeout", + "./scripts/init-development.sh" + ], + "prepare": "./scripts/prepare.sh", + "build": "./scripts/build.sh", + "no": "phpbrew ext disable startup", + "test": [ + "@test-php", + "@test-ext" + ], + "test-php": [ + "phpbrew ext disable startup", + "MINBABY_TEST_EXT=0 ./vendor/bin/kahlan" + ], + "test-ext": [ + "@build", + "phpbrew ext enable startup", + "MINBABY_TEST_EXT=1 ./vendor/bin/kahlan" + ], + "test-ext-vagrind": [ + "@build", + "phpbrew ext enable startup", + "MINBABY_TEST_EXT=1 USE_ZEND_ALLOC=0 valgrind --log-file=/tmp/valgrind.log php ./vendor/bin/kahlan" + ] } } diff --git a/composer.lock b/composer.lock index 0a370b5..15be2dd 100644 --- a/composer.lock +++ b/composer.lock @@ -4,25 +4,25 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b7d7f74907b4b4c4618804584733febe", + "content-hash": "fb818576c980ca61900205c0af9fafc2", "packages": [], "packages-dev": [ { "name": "kahlan/kahlan", - "version": "4.3.1", + "version": "4.7.5", "source": { "type": "git", "url": "https://github.com/kahlan/kahlan.git", - "reference": "92dbe4691f2f3049523cc525365a1a0be0c86db9" + "reference": "c529ef24201053ba76d3c8c3531acd76b629ce87" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/kahlan/kahlan/zipball/92dbe4691f2f3049523cc525365a1a0be0c86db9", - "reference": "92dbe4691f2f3049523cc525365a1a0be0c86db9", + "url": "https://api.github.com/repos/kahlan/kahlan/zipball/c529ef24201053ba76d3c8c3531acd76b629ce87", + "reference": "c529ef24201053ba76d3c8c3531acd76b629ce87", "shasum": "", "mirrors": [ { - "url": "https://dl.laravel-china.org/%package%/%reference%.%type%", + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", "preferred": true } ] @@ -31,7 +31,7 @@ "php": ">=5.5" }, "require-dev": { - "squizlabs/php_codesniffer": "^2.7" + "squizlabs/php_codesniffer": "^3.4" }, "bin": [ "bin/kahlan" @@ -65,7 +65,7 @@ "testing", "unit test" ], - "time": "2018-09-28T15:39:00+00:00" + "time": "2020-04-25T21:27:19+00:00" } ], "aliases": [], @@ -73,6 +73,10 @@ "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, - "platform": [], - "platform-dev": [] + "platform": { + "php": "^7.0", + "ext-json": "*" + }, + "platform-dev": [], + "plugin-api-version": "1.1.0" } diff --git a/config.m4 b/config.m4 index 195e98c..5759bdf 100644 --- a/config.m4 +++ b/config.m4 @@ -17,6 +17,13 @@ PHP_ARG_ENABLE(startup, whether to enable startup support, dnl Make sure that the comment is aligned: [ --enable-startup Enable startup support]) +if test -z "$PHP_DEBUG"; then + AC_ARG_ENABLE(debug, + [--enable-debug compile with debugging system], + [PHP_DEBUG=$enableval], [PHP_DEBUG=no] + ) +fi + if test "$PHP_STARTUP" != "no"; then dnl Write more examples of tests here... @@ -59,5 +66,30 @@ if test "$PHP_STARTUP" != "no"; then dnl dnl PHP_SUBST(STARTUP_SHARED_LIBADD) - PHP_NEW_EXTENSION(startup, startup.c src/ext/test.class.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) + source="startup.c \ + src/ext/functions.c \ + src/ext/test.class.c \ + src/ext/stringy/stringy.c"; + + PHP_NEW_EXTENSION(startup, $source, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) + + dnl configure can't use ".." as a source filename, so we make a link here + # ln -sf $ext_srcdir/../common $ext_srcdir + # ln -sf $ext_srcdir/../deps $ext_srcdir + # ln -sf ./thirdParty/rxi-log include/rxi-log + + dnl add common include path + PHP_ADD_INCLUDE(thirdParty/rxi-log/src) + + # PHP_ADD_SOURCES(thirdParty/rxi-log/src) + + PHP_ADD_MAKEFILE_FRAGMENT fi + +if test "$PHP_DEBUG" != "no"; then + dnl 是,则设置 C 语言宏指令 + AC_DEFINE(PHP_DEBUG, 1, [Include debugging support in ext1]) + AC_DEFINE(PHP_EXT_DEBUG, 1, [Include debugging support in ext]) + fi + + diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..75d539b --- /dev/null +++ b/docs/README.md @@ -0,0 +1,26 @@ +# 一些备忘操作 + +## 数组操作 + +|php语法|c语法(arr是zval*)|说明| +|-|-|-| +|$arr = array() |array_init(arr)|初始化一个新数组, 还有其他方式| +|$arr[] = NULL| add_next_index_null(arr)|| +|$arr[] = 42|add_next_index_long(arr, 42)|| +|$arr[] = true|add_next_index_bool(arr, 1)|| +|$arr[] = 3.14|add_next_index_double(arr, 3.14)|| +|$arr[] = 'foo'|add_next_index_string(arr, "foo", 1)|| +|$arr[] = $myvar|add_next_index_zval(arr, myvar)|| +|$arr[0] = NULL|add_index_null(arr, 0)|| +|$arr[1] = 42|add_index_long(arr, 1, 42)|| +|$arr[2] = true|add_index_bool(arr, 2, 1)|| +|$arr[3] = 3.14|add_index_double(arr, 3, 3.14)|| +|$arr[5] = $myvar|add_index_zval(arr, 5, myvar)|| +|$arr['abc'] = NULL|add_assoc_null(arr, "abc")|| +|$arr['def'] = 711|add_assoc_long(arr, "def", 711)|| +|$arr['ghi'] = true|add_assoc_bool(arr, "ghi", 1)|???| +|$arr['jkl'] = 1.44|add_assoc_double(arr, "jkl", 1.44)|| +|$arr['mno'] = 'baz'|add_assoc_string(arr, "mno", "baz", 1)|| +|$arr['pqr'] = $myvar|add_assoc_zval(arr, "pqr", myvar)|| + +参考:https://hongweipeng.com/index.php/archives/1014/ \ No newline at end of file diff --git a/docs/jobs/stringy.md b/docs/jobs/stringy.md new file mode 100644 index 0000000..156a4ea --- /dev/null +++ b/docs/jobs/stringy.md @@ -0,0 +1,199 @@ +# stringy + +## 进度 + +Stringy library + +https://github.com/danielstjules/Stringy + +- [x] __construct + +- [x] create + +- [x] __toString + +- [x] append + +- [x] at + +- [x] between + +- [x] camelize + +- [x] chars + +- [x] contains + +- [x] containsAll + +- [x] containsAny + +- [x] count + +- [x] countSubstr + +- [x] delimit + +- [x] dasherize + +- [x] endsWith + +- [x] endsWithAny + +- [x] ensureLeft + +- [x] ensureRight + +- [x] first + +- [x] getEncoding + +- [x] getIterator + +- [x] hasLowerCase + +- [x] hasUpperCase + +- [x] htmlDecode + +- [x] htmlEncode + +- [x] humanize + +- [x] indexOf + +- [x] indexOfLast + +- [x] insert + +- [x] isAlpha + +- [x] isAlphanumeric + +- [x] isBlank + +- [x] isHexadecimal + +- [x] isJson + +- [x] isLowerCase + +- [x] isSerialized + +- [x] isBase64 + +- [x] isUpperCase + +- [x] last + +- [x] length + +- [x] lines + +- [x] longestCommonPrefix + +- [x] longestCommonSuffix + +- [ ] longestCommonSubstring + +- [x] lowerCaseFirst + +- [x] offsetExists + +- [x] offsetGet + +- [x] offsetSet + +- [x] offsetUnset + +- [x] pad + +- [x] padBoth + +- [x] padLeft + +- [x] padRight + +- [x] applyPadding + +- [x] prepend + +- [x] regexReplace + +- [x] removeLeft + +- [x] removeRight + +- [x] repeat + +- [x] replace + +- [x] reverse + +- [x] safeTruncate + +- [x] shuffle + +- [ ] slugify + +- [x] startsWith + +- [ ] startsWithAny + +- [ ] slice + +- [x] split + +- [ ] stripWhitespace + +- [x] substr + +- [ ] surround + +- [x] swapCase + +- [ ] tidy + +- [x] titleize + +- [x] toAscii + +- [x] toBoolean + +- [x] toLowerCase + +- [x] toSpaces + +- [x] toTabs + +- [x] toTitleCase + +- [x] toUpperCase + +- [x] trim + +- [x] trimLeft + +- [x] trimRight + +- [x] truncate + +- [x] underscored + +- [x] upperCaseFirst + +- [x] charsArray + +- [x] langSpecificCharsArray + +- [x] matchesPattern + +- [x] eregReplace + +- [x] regexEncoding + +- [x] supportsEncoding + +- [x] collapseWhiteSpace + +- [ ] 测试 \ No newline at end of file diff --git a/kahlan-config.php b/kahlan-config.php index b3d9bbc..4d2e5d4 100644 --- a/kahlan-config.php +++ b/kahlan-config.php @@ -1 +1,10 @@ commandLine(); +$commandLine->option('ff', 'default', 1); +if (isset($_SERVER['CI']) && $_SERVER['CI'] === true) { + $commandLine->option('clover', 'default', 'coverage.xml'); + $commandLine->option('reporter', 'default', 'verbose'); +} diff --git a/php_startup.h b/php_startup.h index 4ea7092..b768a76 100644 --- a/php_startup.h +++ b/php_startup.h @@ -24,7 +24,7 @@ extern zend_module_entry startup_module_entry; #define phpext_startup_ptr &startup_module_entry -#define PHP_STARTUP_VERSION "0.1.0" /* Replace with version number for your extension */ +#define PHP_STARTUP_VERSION "0.2.0" /* Replace with version number for your extension */ #ifdef PHP_WIN32 # define PHP_STARTUP_API __declspec(dllexport) diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000..91cc53f --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -e + +pwd + +echo "disabled startup" +# phpbrew ext disable startup # 如果 ext 出问题了,这个命令就无法执行了 +rm -f $PHPBREW_ROOT/php/$PHPBREW_PHP/var/db/start.ini + +echo "build" +make && make install + +# echo "enabled startup" +# phpbrew ext enable startup diff --git a/scripts/clean.sh b/scripts/clean.sh new file mode 100755 index 0000000..2d7c9ea --- /dev/null +++ b/scripts/clean.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e +set -x + +echo "clean..." +[[ -f Makefile ]] && make clean && phpize --clean diff --git a/scripts/codecov.sh b/scripts/codecov.sh new file mode 100755 index 0000000..1ef332b --- /dev/null +++ b/scripts/codecov.sh @@ -0,0 +1,1550 @@ +#!/usr/bin/env bash + +# Apache License Version 2.0, January 2004 +# https://github.com/codecov/codecov-bash/blob/master/LICENSE + + +set -e +o pipefail + +VERSION="0b37652" + +url="https://codecov.io" +env="$CODECOV_ENV" +service="" +token="" +search_in="" +flags="" +exit_with=0 +curlargs="" +curlawsargs="" +dump="0" +clean="0" +curl_s="-s" +name="$CODECOV_NAME" +include_cov="" +exclude_cov="" +ddp="$(echo ~)/Library/Developer/Xcode/DerivedData" +xp="" +files="" +cacert="$CODECOV_CA_BUNDLE" +gcov_ignore="-not -path './bower_components/**' -not -path './node_modules/**' -not -path './vendor/**'" +gcov_include="" + +ft_gcov="1" +ft_coveragepy="1" +ft_fix="1" +ft_search="1" +ft_s3="1" +ft_network="1" +ft_xcodellvm="1" +ft_xcodeplist="0" + +_git_root=$(git rev-parse --show-toplevel 2>/dev/null || hg root 2>/dev/null || echo $PWD) +git_root="$_git_root" +codecov_yml="" +remote_addr="" +if [ "$git_root" = "$PWD" ]; +then + git_root="." +fi + +url_o="" +pr_o="" +build_o="" +commit_o="" +search_in_o="" +tag_o="" +branch_o="" +slug_o="" +prefix_o="" + +commit="$VCS_COMMIT_ID" +branch="$VCS_BRANCH_NAME" +pr="$VCS_PULL_REQUEST" +slug="$VCS_SLUG" +tag="$VCS_TAG" +build_url="$CI_BUILD_URL" +build="$CI_BUILD_ID" +job="$CI_JOB_ID" + +beta_xcode_partials="" + +proj_root="$git_root" +gcov_exe="gcov" +gcov_arg="" + +b="\033[0;36m" +g="\033[0;32m" +r="\033[0;31m" +e="\033[0;90m" +x="\033[0m" + +show_help() { +cat << EOF + + Codecov Bash $VERSION + + Global report uploading tool for Codecov + Documentation at https://docs.codecov.io/docs + Contribute at https://github.com/codecov/codecov-bash + + + -h Display this help and exit + -f FILE Target file(s) to upload + + -f "path/to/file" only upload this file + skips searching unless provided patterns below + + -f '!*.bar' ignore all files at pattern *.bar + -f '*.foo' include all files at pattern *.foo + Must use single quotes. + This is non-exclusive, use -s "*.foo" to match specific paths. + + -s DIR Directory to search for coverage reports. + Already searches project root and artifact folders. + -t TOKEN Set the private repository token + (option) set environment variable CODECOV_TOKEN=:uuid + + -t @/path/to/token_file + -t uuid + + -n NAME Custom defined name of the upload. Visible in Codecov UI + + -e ENV Specify environment variables to be included with this build + Also accepting environment variables: CODECOV_ENV=VAR,VAR2 + + -e VAR,VAR2 + + -X feature Toggle functionalities + + -X gcov Disable gcov + -X coveragepy Disable python coverage + -X fix Disable report fixing + -X search Disable searching for reports + -X xcode Disable xcode processing + -X network Disable uploading the file network + + -R root dir Used when not in git/hg project to identify project root directory + -y conf file Used to specify the location of the .codecov.yml config file + -F flag Flag the upload to group coverage metrics + + -F unittests This upload is only unittests + -F integration This upload is only integration tests + -F ui,chrome This upload is Chrome - UI tests + + -c Move discovered coverage reports to the trash + -Z Exit with 1 if not successful. Default will Exit with 0 + + -- xcode -- + -D Custom Derived Data Path for Coverage.profdata and gcov processing + Default '~/Library/Developer/Xcode/DerivedData' + -J Specify packages to build coverage. + This can significantly reduces time to build coverage reports. + + -J 'MyAppName' Will match "MyAppName" and "MyAppNameTests" + -J '^ExampleApp$' Will match only "ExampleApp" not "ExampleAppTests" + + -- gcov -- + -g GLOB Paths to ignore during gcov gathering + -G GLOB Paths to include during gcov gathering + -p dir Project root directory + Also used when preparing gcov + -k prefix Prefix filepaths to help resolve path fixing: https://github.com/codecov/support/issues/472 + -x gcovexe gcov executable to run. Defaults to 'gcov' + -a gcovargs extra arguments to pass to gcov + + -- Override CI Environment Variables -- + These variables are automatically detected by popular CI providers + + -B branch Specify the branch name + -C sha Specify the commit sha + -P pr Specify the pull request number + -b build Specify the build number + -T tag Specify the git tag + + -- Enterprise -- + -u URL Set the target url for Enterprise customers + Not required when retrieving the bash uploader from your CCE + (option) Set environment variable CODECOV_URL=https://my-hosted-codecov.com + -r SLUG owner/repo slug used instead of the private repo token in Enterprise + (option) set environment variable CODECOV_SLUG=:owner/:repo + (option) set in your codecov.yml "codecov.slug" + -S PATH File path to your cacert.pem file used to verify ssl with Codecov Enterprise (optional) + (option) Set environment variable: CODECOV_CA_BUNDLE="/path/to/ca.pem" + -U curlargs Extra curl arguments to communicate with Codecov. e.g., -U "--proxy http://http-proxy" + -A curlargs Extra curl arguments to communicate with AWS. + + -- Debugging -- + -d Don't upload, but dump upload file to stdout + -K Remove color from the output + -v Verbose mode + +EOF +} + + +say() { + echo -e "$1" +} + + +urlencode() { + echo "$1" | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3- | sed -e 's/%0A//' +} + + +swiftcov() { + _dir=$(dirname "$1" | sed 's/\(Build\).*/\1/g') + for _type in app framework xctest + do + find "$_dir" -name "*.$_type" | while read f + do + _proj=${f##*/} + _proj=${_proj%."$_type"} + if [ "$2" = "" ] || [ "$(echo "$_proj" | grep -i "$2")" != "" ]; + then + say " $g+$x Building reports for $_proj $_type" + dest=$([ -f "$f/$_proj" ] && echo "$f/$_proj" || echo "$f/Contents/MacOS/$_proj") + _proj_name=$(echo "$_proj" | sed -e 's/[[:space:]]//g') + xcrun llvm-cov show $beta_xcode_partials -instr-profile "$1" "$dest" > "$_proj_name.$_type.coverage.txt" \ + || say " ${r}x>${x} llvm-cov failed to produce results for $dest" + fi + done + done +} + + +# Credits to: https://gist.github.com/pkuczynski/8665367 +parse_yaml() { + local prefix=$2 + local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034') + sed -ne "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \ + -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" $1 | + awk -F$fs '{ + indent = length($1)/2; + vname[indent] = $2; + for (i in vname) {if (i > indent) {delete vname[i]}} + if (length($3) > 0) { + vn=""; if (indent > 0) {vn=(vn)(vname[0])("_")} + printf("%s%s%s=\"%s\"\n", "'$prefix'",vn, $2, $3); + } + }' +} + + +if [ $# != 0 ]; +then + while getopts "a:A:b:B:cC:dD:e:f:F:g:G:hJ:k:Kn:p:P:r:R:y:s:S:t:T:u:U:vx:X:Z" o + do + case "$o" in + "a") + gcov_arg=$OPTARG + ;; + "A") + curlawsargs="$OPTARG" + ;; + "b") + build_o="$OPTARG" + ;; + "B") + branch_o="$OPTARG" + ;; + "c") + clean="1" + ;; + "C") + commit_o="$OPTARG" + ;; + "d") + dump="1" + ;; + "D") + ddp="$OPTARG" + ;; + "e") + env="$env,$OPTARG" + ;; + "f") + if [ "${OPTARG::1}" = "!" ]; + then + exclude_cov="$exclude_cov -not -path '${OPTARG:1}'" + + elif [[ "$OPTARG" = *"*"* ]]; + then + include_cov="$include_cov -or -name '$OPTARG'" + + else + ft_search=0 + if [ "$files" = "" ]; + then + files="$OPTARG" + else + files="$files +$OPTARG" + fi + fi + ;; + "F") + if [ "$flags" = "" ]; + then + flags="$OPTARG" + else + flags="$flags,$OPTARG" + fi + ;; + "g") + gcov_ignore="$gcov_ignore -not -path '$OPTARG'" + ;; + "G") + gcov_include="$gcov_include -path '$OPTARG'" + ;; + "h") + show_help + exit 0; + ;; + "J") + ft_xcodellvm="1" + ft_xcodeplist="0" + if [ "$xp" = "" ]; + then + xp="$OPTARG" + else + xp="$xp\|$OPTARG" + fi + ;; + "k") + prefix_o=$(echo "$OPTARG" | sed -e 's:^/*::' -e 's:/*$::') + ;; + "K") + b="" + g="" + r="" + e="" + x="" + ;; + "n") + name="$OPTARG" + ;; + "p") + proj_root="$OPTARG" + ;; + "P") + pr_o="$OPTARG" + ;; + "r") + slug_o="$OPTARG" + ;; + "R") + git_root="$OPTARG" + ;; + "s") + if [ "$search_in_o" = "" ]; + then + search_in_o="$OPTARG" + else + search_in_o="$search_in_o $OPTARG" + fi + ;; + "S") + cacert="--cacert \"$OPTARG\"" + ;; + "t") + if [ "${OPTARG::1}" = "@" ]; + then + token=$(cat "${OPTARG:1}" | tr -d ' \n') + else + token="$OPTARG" + fi + ;; + "T") + tag_o="$OPTARG" + ;; + "u") + url_o=$(echo "$OPTARG" | sed -e 's/\/$//') + ;; + "U") + curlargs="$OPTARG" + ;; + "v") + set -x + curl_s="" + ;; + "x") + gcov_exe=$OPTARG + ;; + "X") + if [ "$OPTARG" = "gcov" ]; + then + ft_gcov="0" + elif [ "$OPTARG" = "coveragepy" ] || [ "$OPTARG" = "py" ]; + then + ft_coveragepy="0" + elif [ "$OPTARG" = "xcodellvm" ]; + then + ft_xcodellvm="1" + ft_xcodeplist="0" + elif [ "$OPTARG" = "fix" ] || [ "$OPTARG" = "fixes" ]; + then + ft_fix="0" + elif [ "$OPTARG" = "xcode" ]; + then + ft_xcodellvm="0" + ft_xcodeplist="0" + elif [ "$OPTARG" = "search" ]; + then + ft_search="0" + elif [ "$OPTARG" = "xcodepartials" ]; + then + beta_xcode_partials="-use-color" + elif [ "$OPTARG" = "network" ]; + then + ft_network="0" + elif [ "$OPTARG" = "s3" ]; + then + ft_s3="0" + fi + ;; + "y") + codecov_yml="$OPTARG" + ;; + "Z") + exit_with=1 + ;; + esac + done +fi + +say " + _____ _ + / ____| | | +| | ___ __| | ___ ___ _____ __ +| | / _ \\ / _\` |/ _ \\/ __/ _ \\ \\ / / +| |___| (_) | (_| | __/ (_| (_) \\ V / + \\_____\\___/ \\__,_|\\___|\\___\\___/ \\_/ + Bash-$VERSION + +" + +search_in="$proj_root" + +if [ "$JENKINS_URL" != "" ]; +then + say "$e==>$x Jenkins CI detected." + # https://wiki.jenkins-ci.org/display/JENKINS/Building+a+software+project + # https://wiki.jenkins-ci.org/display/JENKINS/GitHub+pull+request+builder+plugin#GitHubpullrequestbuilderplugin-EnvironmentVariables + service="jenkins" + + if [ "$ghprbSourceBranch" != "" ]; + then + branch="$ghprbSourceBranch" + elif [ "$GIT_BRANCH" != "" ]; + then + branch="$GIT_BRANCH" + elif [ "$BRANCH_NAME" != "" ]; + then + branch="$BRANCH_NAME" + fi + + if [ "$ghprbActualCommit" != "" ]; + then + commit="$ghprbActualCommit" + elif [ "$GIT_COMMIT" != "" ]; + then + commit="$GIT_COMMIT" + fi + + if [ "$ghprbPullId" != "" ]; + then + pr="$ghprbPullId" + elif [ "$CHANGE_ID" != "" ]; + then + pr="$CHANGE_ID" + fi + + build="$BUILD_NUMBER" + build_url=$(urlencode "$BUILD_URL") + +elif [ "$CI" = "true" ] && [ "$TRAVIS" = "true" ] && [ "$SHIPPABLE" != "true" ]; +then + say "$e==>$x Travis CI detected." + # https://docs.travis-ci.com/user/environment-variables/ + service="travis" + commit="${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT}" + build="$TRAVIS_JOB_NUMBER" + pr="$TRAVIS_PULL_REQUEST" + job="$TRAVIS_JOB_ID" + slug="$TRAVIS_REPO_SLUG" + env="$env,TRAVIS_OS_NAME" + tag="$TRAVIS_TAG" + if [ "$TRAVIS_BRANCH" != "$TRAVIS_TAG" ]; + then + branch="$TRAVIS_BRANCH" + fi + + language=$(printenv | grep "TRAVIS_.*_VERSION" | head -1) + if [ "$language" != "" ]; + then + env="$env,${language%=*}" + fi + +elif [ "$DOCKER_REPO" != "" ]; +then + say "$e==>$x Docker detected." + # https://docs.docker.com/docker-cloud/builds/advanced/ + service="docker" + branch="$SOURCE_BRANCH" + commit="$SOURCE_COMMIT" + slug="$DOCKER_REPO" + tag="$CACHE_TAG" + env="$env,IMAGE_NAME" + +elif [ "$CI" = "true" ] && [ "$CI_NAME" = "codeship" ]; +then + say "$e==>$x Codeship CI detected." + # https://www.codeship.io/documentation/continuous-integration/set-environment-variables/ + service="codeship" + branch="$CI_BRANCH" + build="$CI_BUILD_NUMBER" + build_url=$(urlencode "$CI_BUILD_URL") + commit="$CI_COMMIT_ID" + +elif [ ! -z "$CF_BUILD_URL" ] && [ ! -z "$CF_BUILD_ID" ]; +then + say "$e==>$x Codefresh CI detected." + # https://docs.codefresh.io/v1.0/docs/variables + service="codefresh" + branch="$CF_BRANCH" + build="$CF_BUILD_ID" + build_url=$(urlencode "$CF_BUILD_URL") + commit="$CF_REVISION" + +elif [ "$TEAMCITY_VERSION" != "" ]; +then + say "$e==>$x TeamCity CI detected." + # https://confluence.jetbrains.com/display/TCD8/Predefined+Build+Parameters + # https://confluence.jetbrains.com/plugins/servlet/mobile#content/view/74847298 + if [ "$TEAMCITY_BUILD_BRANCH" = '' ]; + then + echo " Teamcity does not automatically make build parameters available as environment variables." + echo " Add the following environment parameters to the build configuration" + echo " env.TEAMCITY_BUILD_BRANCH = %teamcity.build.branch%" + echo " env.TEAMCITY_BUILD_ID = %teamcity.build.id%" + echo " env.TEAMCITY_BUILD_URL = %teamcity.serverUrl%/viewLog.html?buildId=%teamcity.build.id%" + echo " env.TEAMCITY_BUILD_COMMIT = %system.build.vcs.number%" + echo " env.TEAMCITY_BUILD_REPOSITORY = %vcsroot..url%" + fi + service="teamcity" + branch="$TEAMCITY_BUILD_BRANCH" + build="$TEAMCITY_BUILD_ID" + build_url=$(urlencode "$TEAMCITY_BUILD_URL") + if [ "$TEAMCITY_BUILD_COMMIT" != "" ]; + then + commit="$TEAMCITY_BUILD_COMMIT" + else + commit="$BUILD_VCS_NUMBER" + fi + remote_addr="$TEAMCITY_BUILD_REPOSITORY" + +elif [ "$CI" = "true" ] && [ "$CIRCLECI" = "true" ]; +then + say "$e==>$x Circle CI detected." + # https://circleci.com/docs/environment-variables + service="circleci" + branch="$CIRCLE_BRANCH" + build="$CIRCLE_BUILD_NUM" + job="$CIRCLE_NODE_INDEX" + if [ "$CIRCLE_PROJECT_REPONAME" != "" ]; + then + slug="$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME" + else + # git@github.com:owner/repo.git + slug="${CIRCLE_REPOSITORY_URL##*:}" + # owner/repo.git + slug="${slug%%.git}" + fi + pr="$CIRCLE_PR_NUMBER" + commit="$CIRCLE_SHA1" + search_in="$search_in $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS" + +elif [ "$BUDDYBUILD_BRANCH" != "" ]; +then + say "$e==>$x buddybuild detected" + # http://docs.buddybuild.com/v6/docs/custom-prebuild-and-postbuild-steps + service="buddybuild" + branch="$BUDDYBUILD_BRANCH" + build="$BUDDYBUILD_BUILD_NUMBER" + build_url="https://dashboard.buddybuild.com/public/apps/$BUDDYBUILD_APP_ID/build/$BUDDYBUILD_BUILD_ID" + # BUDDYBUILD_TRIGGERED_BY + if [ "$ddp" = "$(echo ~)/Library/Developer/Xcode/DerivedData" ]; + then + ddp="/private/tmp/sandbox/${BUDDYBUILD_APP_ID}/bbtest" + fi + +elif [ "${bamboo_planRepository_revision}" != "" ]; +then + say "$e==>$x Bamboo detected" + # https://confluence.atlassian.com/bamboo/bamboo-variables-289277087.html#Bamboovariables-Build-specificvariables + service="bamboo" + commit="${bamboo_planRepository_revision}" + branch="${bamboo_planRepository_branch}" + build="${bamboo_buildNumber}" + build_url="${bamboo_buildResultsUrl}" + remote_addr="${bamboo_planRepository_repositoryUrl}" + +elif [ "$CI" = "true" ] && [ "$BITRISE_IO" = "true" ]; +then + # http://devcenter.bitrise.io/faq/available-environment-variables/ + say "$e==>$x Bitrise CI detected." + service="bitrise" + branch="$BITRISE_GIT_BRANCH" + build="$BITRISE_BUILD_NUMBER" + build_url=$(urlencode "$BITRISE_BUILD_URL") + pr="$BITRISE_PULL_REQUEST" + if [ "$GIT_CLONE_COMMIT_HASH" != "" ]; + then + commit="$GIT_CLONE_COMMIT_HASH" + fi + +elif [ "$CI" = "true" ] && [ "$SEMAPHORE" = "true" ]; +then + say "$e==>$x Semaphore CI detected." + # https://semaphoreapp.com/docs/available-environment-variables.html + service="semaphore" + branch="$BRANCH_NAME" + build="$SEMAPHORE_BUILD_NUMBER" + job="$SEMAPHORE_CURRENT_THREAD" + pr="$PULL_REQUEST_NUMBER" + slug="$SEMAPHORE_REPO_SLUG" + commit="$REVISION" + env="$env,SEMAPHORE_TRIGGER_SOURCE" + +elif [ "$CI" = "true" ] && [ "$BUILDKITE" = "true" ]; +then + say "$e==>$x Buildkite CI detected." + # https://buildkite.com/docs/guides/environment-variables + service="buildkite" + branch="$BUILDKITE_BRANCH" + build="$BUILDKITE_BUILD_NUMBER" + job="$BUILDKITE_JOB_ID" + build_url=$(urlencode "$BUILDKITE_BUILD_URL") + slug="$BUILDKITE_PROJECT_SLUG" + commit="$BUILDKITE_COMMIT" + if [[ "$BUILDKITE_PULL_REQUEST" != "false" ]]; then + pr="$BUILDKITE_PULL_REQUEST" + fi + tag="$BUILDKITE_TAG" + +elif [ "$CI" = "drone" ] || [ "$DRONE" = "true" ]; +then + say "$e==>$x Drone CI detected." + # http://docs.drone.io/env.html + # drone commits are not full shas + service="drone.io" + branch="$DRONE_BRANCH" + build="$DRONE_BUILD_NUMBER" + build_url=$(urlencode "${DRONE_BUILD_LINK}") + pr="$DRONE_PULL_REQUEST" + job="$DRONE_JOB_NUMBER" + tag="$DRONE_TAG" + +elif [ "$HEROKU_TEST_RUN_BRANCH" != "" ]; +then + say "$e==>$x Heroku CI detected." + # https://devcenter.heroku.com/articles/heroku-ci#environment-variables + service="heroku" + branch="$HEROKU_TEST_RUN_BRANCH" + build="$HEROKU_TEST_RUN_ID" + +elif [ "$CI" = "True" ] && [ "$APPVEYOR" = "True" ]; +then + say "$e==>$x Appveyor CI detected." + # http://www.appveyor.com/docs/environment-variables + service="appveyor" + branch="$APPVEYOR_REPO_BRANCH" + build=$(urlencode "$APPVEYOR_JOB_ID") + pr="$APPVEYOR_PULL_REQUEST_NUMBER" + job="$APPVEYOR_ACCOUNT_NAME%2F$APPVEYOR_PROJECT_SLUG%2F$APPVEYOR_BUILD_VERSION" + slug="$APPVEYOR_REPO_NAME" + commit="$APPVEYOR_REPO_COMMIT" + +elif [ "$CI" = "true" ] && [ "$WERCKER_GIT_BRANCH" != "" ]; +then + say "$e==>$x Wercker CI detected." + # http://devcenter.wercker.com/articles/steps/variables.html + service="wercker" + branch="$WERCKER_GIT_BRANCH" + build="$WERCKER_MAIN_PIPELINE_STARTED" + slug="$WERCKER_GIT_OWNER/$WERCKER_GIT_REPOSITORY" + commit="$WERCKER_GIT_COMMIT" + +elif [ "$CI" = "true" ] && [ "$MAGNUM" = "true" ]; +then + say "$e==>$x Magnum CI detected." + # https://magnum-ci.com/docs/environment + service="magnum" + branch="$CI_BRANCH" + build="$CI_BUILD_NUMBER" + commit="$CI_COMMIT" + +elif [ "$SHIPPABLE" = "true" ]; +then + say "$e==>$x Shippable CI detected." + # http://docs.shippable.com/ci_configure/ + service="shippable" + branch=$([ "$HEAD_BRANCH" != "" ] && echo "$HEAD_BRANCH" || echo "$BRANCH") + build="$BUILD_NUMBER" + build_url=$(urlencode "$BUILD_URL") + pr="$PULL_REQUEST" + slug="$REPO_FULL_NAME" + commit="$COMMIT" + +elif [ "$TDDIUM" = "true" ]; +then + say "Solano CI detected." + # http://docs.solanolabs.com/Setup/tddium-set-environment-variables/ + service="solano" + commit="$TDDIUM_CURRENT_COMMIT" + branch="$TDDIUM_CURRENT_BRANCH" + build="$TDDIUM_TID" + pr="$TDDIUM_PR_ID" + +elif [ "$GREENHOUSE" = "true" ]; +then + say "$e==>$x Greenhouse CI detected." + # http://docs.greenhouseci.com/docs/environment-variables-files + service="greenhouse" + branch="$GREENHOUSE_BRANCH" + build="$GREENHOUSE_BUILD_NUMBER" + build_url=$(urlencode "$GREENHOUSE_BUILD_URL") + pr="$GREENHOUSE_PULL_REQUEST" + commit="$GREENHOUSE_COMMIT" + search_in="$search_in $GREENHOUSE_EXPORT_DIR" + +elif [ "$GITLAB_CI" != "" ]; +then + say "$e==>$x GitLab CI detected." + # http://doc.gitlab.com/ce/ci/variables/README.html + service="gitlab" + branch="${CI_BUILD_REF_NAME:-$CI_COMMIT_REF_NAME}" + build="${CI_BUILD_ID:-$CI_JOB_ID}" + remote_addr="${CI_BUILD_REPO:-$CI_REPOSITORY_URL}" + commit="${CI_BUILD_REF:-$CI_COMMIT_SHA}" + +else + say "${r}x>${x} No CI provider detected." + say " Testing inside Docker? ${b}http://docs.codecov.io/docs/testing-with-docker${x}" + say " Testing with Tox? ${b}https://docs.codecov.io/docs/python#section-testing-with-tox${x}" + +fi + +say " ${e}project root:${x} $git_root" + +# find branch, commit, repo from git command +if [ "$GIT_BRANCH" != "" ]; +then + branch="$GIT_BRANCH" + +elif [ "$branch" = "" ]; +then + branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || hg branch 2>/dev/null || echo "") + if [ "$branch" = "HEAD" ]; + then + branch="" + fi +fi + +if [ "$commit_o" = "" ]; +then + # merge commit -> actual commit + mc= + if [ -n "$pr" ] && [ "$pr" != false ]; + then + mc=$(git show --no-patch --format="%P" 2>/dev/null || echo "") + fi + if [[ "$mc" =~ ^[a-z0-9]{40}[[:space:]][a-z0-9]{40}$ ]]; + then + say " Fixing merge commit SHA" + commit=$(echo "$mc" | cut -d' ' -f2) + elif [ "$GIT_COMMIT" != "" ]; + then + commit="$GIT_COMMIT" + elif [ "$commit" = "" ]; + then + commit=$(git log -1 --format="%H" 2>/dev/null || hg id -i --debug 2>/dev/null | tr -d '+' || echo "") + fi +else + commit="$commit_o" +fi + +if [ "$CODECOV_TOKEN" != "" ] && [ "$token" = "" ]; +then + say "${e}-->${x} token set from env" + token="$CODECOV_TOKEN" +fi + +if [ "$CODECOV_URL" != "" ] && [ "$url_o" = "" ]; +then + say "${e}-->${x} url set from env" + url_o=$(echo "$CODECOV_URL" | sed -e 's/\/$//') +fi + +if [ "$CODECOV_SLUG" != "" ]; +then + say "${e}-->${x} slug set from env" + slug_o="$CODECOV_SLUG" + +elif [ "$slug" = "" ]; +then + if [ "$remote_addr" = "" ]; + then + remote_addr=$(git config --get remote.origin.url || hg paths default || echo '') + fi + if [ "$remote_addr" != "" ]; + then + if echo "$remote_addr" | grep -q "//"; then + # https + slug=$(echo "$remote_addr" | cut -d / -f 4,5 | sed -e 's/\.git$//') + else + # ssh + slug=$(echo "$remote_addr" | cut -d : -f 2 | sed -e 's/\.git$//') + fi + fi + if [ "$slug" = "/" ]; + then + slug="" + fi +fi + +yaml=$(test -n "$codecov_yml" && echo "$codecov_yml" \ + || cd "$git_root" && \ + git ls-files "*codecov.yml" "*codecov.yaml" 2>/dev/null \ + || hg locate "*codecov.yml" "*codecov.yaml" 2>/dev/null \ + || cd $proj_root && find . -type f -name '*codecov.y*ml' -depth 1 2>/dev/null \ + || echo '') +yaml=$(echo "$yaml" | head -1) + +if [ "$yaml" != "" ]; +then + say " ${e}Yaml found at:${x} $yaml" + config=$(parse_yaml "$git_root/$yaml" || echo '') + + # TODO validate the yaml here + + if [ "$(echo "$config" | grep 'codecov_token="')" != "" ] && [ "$token" = "" ]; + then + say "${e}-->${x} token set from yaml" + token="$(echo "$config" | grep 'codecov_token="' | sed -e 's/codecov_token="//' | sed -e 's/"\.*//')" + fi + + if [ "$(echo "$config" | grep 'codecov_url="')" != "" ] && [ "$url_o" = "" ]; + then + say "${e}-->${x} url set from yaml" + url_o="$(echo "$config" | grep 'codecov_url="' | sed -e 's/codecov_url="//' | sed -e 's/"\.*//')" + fi + + if [ "$(echo "$config" | grep 'codecov_slug="')" != "" ] && [ "$slug_o" = "" ]; + then + say "${e}-->${x} slug set from yaml" + slug_o="$(echo "$config" | grep 'codecov_slug="' | sed -e 's/codecov_slug="//' | sed -e 's/"\.*//')" + fi +else + say " ${g}Yaml not found, that's ok! Learn more at${x} ${b}http://docs.codecov.io/docs/codecov-yaml${x}" + +fi + +if [ "$branch_o" != "" ]; +then + branch=$(urlencode "$branch_o") +else + branch=$(urlencode "$branch") +fi + +query="branch=$branch\ + &commit=$commit\ + &build=$([ "$build_o" = "" ] && echo "$build" || echo "$build_o")\ + &build_url=$build_url\ + &name=$(urlencode "$name")\ + &tag=$([ "$tag_o" = "" ] && echo "$tag" || echo "$tag_o")\ + &slug=$([ "$slug_o" = "" ] && urlencode "$slug" || urlencode "$slug_o")\ + &service=$service\ + &flags=$flags\ + &pr=$([ "$pr_o" = "" ] && echo "${pr##\#}" || echo "${pr_o##\#}")\ + &job=$job" + +if [ "$ft_search" = "1" ]; +then + # detect bower comoponents location + bower_components="bower_components" + bower_rc=$(cd "$git_root" && cat .bowerrc 2>/dev/null || echo "") + if [ "$bower_rc" != "" ]; + then + bower_components=$(echo "$bower_rc" | tr -d '\n' | grep '"directory"' | cut -d'"' -f4 | sed -e 's/\/$//') + if [ "$bower_components" = "" ]; + then + bower_components="bower_components" + fi + fi + + # Swift Coverage + if [ "$ft_xcodellvm" = "1" ] && [ -d "$ddp" ]; + then + say "${e}==>${x} Processing Xcode reports via llvm-cov" + say " DerivedData folder: $ddp" + profdata_files=$(find "$ddp" -name '*.profdata' 2>/dev/null || echo '') + if [ "$profdata_files" != "" ]; + then + # xcode via profdata + if [ "$xp" = "" ]; + then + # xp=$(xcodebuild -showBuildSettings 2>/dev/null | grep -i "^\s*PRODUCT_NAME" | sed -e 's/.*= \(.*\)/\1/') + # say " ${e}->${x} Speed up Xcode processing by adding ${e}-J '$xp'${x}" + say " ${g}hint${x} Speed up Swift processing by using use ${g}-J 'AppName'${x} (regexp accepted)" + say " ${g}hint${x} This will remove Pods/ from your report. Also ${b}https://docs.codecov.io/docs/ignoring-paths${x}" + fi + while read -r profdata; + do + if [ "$profdata" != "" ]; + then + swiftcov "$profdata" "$xp" + fi + done <<< "$profdata_files" + else + say " ${e}->${x} No Swift coverage found" + fi + + # Obj-C Gcov Coverage + if [ "$ft_gcov" = "1" ]; + then + say " ${e}->${x} Running $gcov_exe for Obj-C" + bash -c "find $ddp -type f -name '*.gcda' $gcov_include $gcov_ignore -exec $gcov_exe -p $gcov_arg {} +" || true + fi + fi + + if [ "$ft_xcodeplist" = "1" ] && [ -d "$ddp" ]; + then + say "${e}==>${x} Processing Xcode plists" + plists_files=$(find "$ddp" -name '*.xccoverage' 2>/dev/null || echo '') + if [ "$plists_files" != "" ]; + then + while read -r plist; + do + if [ "$plist" != "" ]; + then + say " ${g}Found${x} plist file at $plist" + plutil -convert xml1 -o "$(basename "$plist").plist" -- $plist + fi + done <<< "$plists_files" + fi + fi + + # Gcov Coverage + if [ "$ft_gcov" = "1" ]; + then + say "${e}==>${x} Running gcov in $proj_root ${e}(disable via -X gcov)${x}" + bash -c "find $proj_root -type f -name '*.gcno' $gcov_include $gcov_ignore -exec $gcov_exe -pb $gcov_arg {} +" || true + else + say "${e}==>${x} gcov disabled" + fi + + # Python Coverage + if [ "$ft_coveragepy" = "1" ]; + then + if [ ! -f coverage.xml ]; + then + if which coverage >/dev/null 2>&1; + then + say "${e}==>${x} Python coveragepy exists ${e}disable via -X coveragepy${x}" + + dotcoverage=$(find "$git_root" -name '.coverage' -or -name '.coverage.*' | head -1 || echo '') + if [ "$dotcoverage" != "" ]; + then + cd "$(dirname "$dotcoverage")" + if [ ! -f .coverage ]; + then + say " ${e}->${x} Running coverage combine" + coverage combine -a + fi + say " ${e}->${x} Running coverage xml" + if [ "$(coverage xml -i)" != "No data to report." ]; + then + files="$files +$PWD/coverage.xml" + else + say " ${r}No data to report.${x}" + fi + cd "$proj_root" + else + say " ${r}No .coverage file found.${x}" + fi + else + say "${e}==>${x} Python coveragepy not found" + fi + fi + else + say "${e}==>${x} Python coveragepy disabled" + fi + + if [ "$search_in_o" != "" ]; + then + # location override + search_in="$search_in_o" + fi + + say "$e==>$x Searching for coverage reports in:" + for _path in $search_in + do + say " ${g}+${x} $_path" + done + + patterns="find $search_in \( \ + -name vendor \ + -or -name htmlcov \ + -or -name virtualenv \ + -or -name js/generated/coverage \ + -or -name .virtualenv \ + -or -name virtualenvs \ + -or -name .virtualenvs \ + -or -name .env \ + -or -name .envs \ + -or -name env \ + -or -name .yarn-cache \ + -or -name envs \ + -or -name .venv \ + -or -name .venvs \ + -or -name venv \ + -or -name venvs \ + -or -name .git \ + -or -name .hg \ + -or -name .tox \ + -or -name __pycache__ \ + -or -name '.egg-info*' \ + -or -name '$bower_components' \ + -or -name node_modules \ + -or -name 'conftest_*.c.gcov' \ + \) -prune -or \ + -type f \( -name '*coverage*.*' \ + -or -name 'nosetests.xml' \ + -or -name 'jacoco*.xml' \ + -or -name 'clover.xml' \ + -or -name 'report.xml' \ + -or -name '*.codecov.*' \ + -or -name 'codecov.*' \ + -or -name 'cobertura.xml' \ + -or -name 'excoveralls.json' \ + -or -name 'luacov.report.out' \ + -or -name 'coverage-final.json' \ + -or -name 'naxsi.info' \ + -or -name 'lcov.info' \ + -or -name 'lcov.dat' \ + -or -name '*.lcov' \ + -or -name '*.clover' \ + -or -name 'cover.out' \ + -or -name 'gcov.info' \ + -or -name '*.gcov' \ + -or -name '*.lst' \ + $include_cov \) \ + $exclude_cov \ + -not -name '*.profdata' \ + -not -name 'coverage-summary.json' \ + -not -name 'phpunit-code-coverage.xml' \ + -not -name '*/classycle/report.xml' \ + -not -name 'remapInstanbul.coverage*.json' \ + -not -name 'phpunit-coverage.xml' \ + -not -name '*codecov.yml' \ + -not -name '*.serialized' \ + -not -name '.coverage*' \ + -not -name '.*coveragerc' \ + -not -name '*.sh' \ + -not -name '*.bat' \ + -not -name '*.ps1' \ + -not -name '*.env' \ + -not -name '*.cmake' \ + -not -name '*.dox' \ + -not -name '*.ec' \ + -not -name '*.rst' \ + -not -name '*.h' \ + -not -name '*.scss' \ + -not -name '*.o' \ + -not -name '*.proto' \ + -not -name '*.sbt' \ + -not -name '*.xcoverage.*' \ + -not -name '*.gz' \ + -not -name '*.conf' \ + -not -name '*.p12' \ + -not -name '*.csv' \ + -not -name '*.rsp' \ + -not -name '*.m4' \ + -not -name '*.pem' \ + -not -name '*~' \ + -not -name '*.exe' \ + -not -name '*.am' \ + -not -name '*.template' \ + -not -name '*.cp' \ + -not -name '*.bw' \ + -not -name '*.crt' \ + -not -name '*.log' \ + -not -name '*.cmake' \ + -not -name '*.pth' \ + -not -name '*.in' \ + -not -name '*.jar*' \ + -not -name '*.pom*' \ + -not -name '*.png' \ + -not -name '*.jpg' \ + -not -name '*.sql' \ + -not -name '*.jpeg' \ + -not -name '*.svg' \ + -not -name '*.gif' \ + -not -name '*.csv' \ + -not -name '*.snapshot' \ + -not -name '*.mak*' \ + -not -name '*.bash' \ + -not -name '*.data' \ + -not -name '*.py' \ + -not -name '*.class' \ + -not -name '*.xcconfig' \ + -not -name '*.ec' \ + -not -name '*.coverage' \ + -not -name '*.pyc' \ + -not -name '*.cfg' \ + -not -name '*.egg' \ + -not -name '*.ru' \ + -not -name '*.css' \ + -not -name '*.less' \ + -not -name '*.pyo' \ + -not -name '*.whl' \ + -not -name '*.html' \ + -not -name '*.ftl' \ + -not -name '*.erb' \ + -not -name '*.rb' \ + -not -name '*.js' \ + -not -name '*.jade' \ + -not -name '*.db' \ + -not -name '*.md' \ + -not -name '*.cpp' \ + -not -name '*.gradle' \ + -not -name '*.tar.tz' \ + -not -name '*.scss' \ + -not -name 'include.lst' \ + -not -name 'fullLocaleNames.lst' \ + -not -name 'inputFiles.lst' \ + -not -name 'createdFiles.lst' \ + -not -name 'scoverage.measurements.*' \ + -not -name 'test_*_coverage.txt' \ + -not -name 'testrunner-coverage*' \ + -print 2>/dev/null" + files=$(eval "$patterns" || echo '') + +elif [ "$include_cov" != "" ]; +then + files=$(eval "find $search_in -type f \( ${include_cov:5} \)$exclude_cov 2>/dev/null" || echo '') +fi + +num_of_files=$(echo "$files" | wc -l | tr -d ' ') +if [ "$num_of_files" != '' ] && [ "$files" != '' ]; +then + say " ${e}->${x} Found $num_of_files reports" +fi + +# no files found +if [ "$files" = "" ]; +then + say "${r}-->${x} No coverage report found." + say " Please visit ${b}http://docs.codecov.io/docs/supported-languages${x}" + exit ${exit_with}; +fi + +if [ "$ft_network" == "1" ]; +then + say "${e}==>${x} Detecting git/mercurial file structure" + network=$(cd "$git_root" && git ls-files 2>/dev/null || hg locate 2>/dev/null || echo "") + if [ "$network" = "" ]; + then + network=$(find "$git_root" \( \ + -name virtualenv \ + -name .virtualenv \ + -name virtualenvs \ + -name .virtualenvs \ + -name '*.png' \ + -name '*.gif' \ + -name '*.jpg' \ + -name '*.jpeg' \ + -name '*.md' \ + -name .env \ + -name .envs \ + -name env \ + -name envs \ + -name .venv \ + -name .venvs \ + -name venv \ + -name venvs \ + -name .git \ + -name .egg-info \ + -name shunit2-2.1.6 \ + -name vendor \ + -name __pycache__ \ + -name node_modules \ + -path '*/$bower_components/*' \ + -path '*/target/delombok/*' \ + -path '*/build/lib/*' \ + -path '*/js/generated/coverage/*' \ + \) -prune -or \ + -type f -print 2>/dev/null || echo '') + fi + + if [ "$prefix_o" != "" ]; + then + network=$(echo "$network" | awk "{print \"$prefix_o/\"\$0}") + fi +fi + +upload_file=`mktemp /tmp/codecov.XXXXXX` +adjustments_file=`mktemp /tmp/codecov.adjustments.XXXXXX` + +cleanup() { + rm -f $upload_file $adjustments_file $upload_file.gz +} + +trap cleanup INT ABRT TERM + +if [ "$env" != "" ]; +then + inc_env="" + say "${e}==>${x} Appending build variables" + for varname in $(echo "$env" | tr ',' ' ') + do + if [ "$varname" != "" ]; + then + say " ${g}+${x} $varname" + inc_env="${inc_env}${varname}=$(eval echo "\$${varname}") +" + fi + done + +echo "$inc_env<<<<<< ENV" >> $upload_file +fi + +# Append git file list +# write discovered yaml location +echo "$yaml" >> $upload_file +if [ "$ft_network" == "1" ]; +then + i="woff|eot|otf" # fonts + i="$i|gif|png|jpg|jpeg|psd" # images + i="$i|ptt|pptx|numbers|pages|md|txt|xlsx|docx|doc|pdf|html|csv" # docs + i="$i|yml|yaml|.gitignore" # supporting docs + echo "$network" | grep -vwE "($i)$" >> $upload_file +fi +echo "<<<<<< network" >> $upload_file + +fr=0 +say "${e}==>${x} Reading reports" +while IFS='' read -r file; +do + # read the coverage file + if [ "$(echo "$file" | tr -d ' ')" != '' ]; + then + if [ -f "$file" ]; + then + report_len=$(wc -c < "$file") + if [ "$report_len" -ne 0 ]; + then + say " ${g}+${x} $file ${e}bytes=$(echo "$report_len" | tr -d ' ')${x}" + # append to to upload + _filename=$(basename "$file") + if [ "${_filename##*.}" = 'gcov' ]; + then + echo "# path=$(echo "$file.reduced" | sed "s|^$git_root/||")" >> $upload_file + # get file name + head -1 $file >> $upload_file + # 1. remove source code + # 2. remove ending bracket lines + # 3. remove whitespace + # 4. remove contextual lines + # 5. remove function names + awk -F': *' '{print $1":"$2":"}' $file \ + | sed '\/: *} *$/d' \ + | sed 's/^ *//' \ + | sed '/^-/d' \ + | sed 's/^function.*/func/' >> $upload_file + else + echo "# path=$(echo "$file" | sed "s|^$git_root/||")" >> $upload_file + cat "$file" >> $upload_file + fi + echo "<<<<<< EOF" >> $upload_file + fr=1 + if [ "$clean" = "1" ]; + then + rm "$file" + fi + else + say " ${r}-${x} Skipping empty file $file" + fi + else + say " ${r}-${x} file not found at $file" + fi + fi +done <<< "$(echo -e "$files")" + +if [ "$fr" = "0" ]; +then + say "${r}-->${x} No coverage data found." + say " Please visit ${b}http://docs.codecov.io/docs/supported-languages${x}" + say " search for your projects language to learn how to collect reports." + exit ${exit_with}; +fi + +if [ "$ft_fix" = "1" ]; +then + say "${e}==>${x} Appending adjustments" + say " ${b}http://docs.codecov.io/docs/fixing-reports${x}" + + empty_line='^[[:space:]]*$' + # // + syntax_comment='^[[:space:]]*//.*' + # /* or */ + syntax_comment_block='^[[:space:]]*(\/\*|\*\/)[[:space:]]*$' + # { or } + syntax_bracket='^[[:space:]]*[\{\}][[:space:]]*(//.*)?$' + # [ or ] + syntax_list='^[[:space:]]*[][][[:space:]]*(//.*)?$' + + skip_dirs="-not -path '*/$bower_components/*' \ + -not -path '*/node_modules/*'" + + cut_and_join() { + awk 'BEGIN { FS=":" } + $3 ~ /\/\*/ || $3 ~ /\*\// { print $0 ; next } + $1!=key { if (key!="") print out ; key=$1 ; out=$1":"$2 ; next } + { out=out","$2 } + END { print out }' 2>/dev/null + } + + if echo "$network" | grep -m1 '.kt$' 1>/dev/null; + then + # skip brackets and comments + find "$git_root" -type f \ + -name '*.kt' \ + -exec \ + grep -nIHE -e $syntax_bracket \ + -e $syntax_comment_block {} \; \ + | cut_and_join \ + >> $adjustments_file \ + || echo '' + + # last line in file + find "$git_root" -type f \ + -name '*.kt' -exec \ + wc -l {} \; \ + | while read l; do echo "EOF: $l"; done \ + 2>/dev/null \ + >> $adjustments_file \ + || echo '' + + fi + + if echo "$network" | grep -m1 '.go$' 1>/dev/null; + then + # skip empty lines, comments, and brackets + find "$git_root" -not -path '*/vendor/*' \ + -type f \ + -name '*.go' \ + -exec \ + grep -nIHE \ + -e $empty_line \ + -e $syntax_comment \ + -e $syntax_comment_block \ + -e $syntax_bracket \ + {} \; \ + | cut_and_join \ + >> $adjustments_file \ + || echo '' + fi + + if echo "$network" | grep -m1 '.dart$' 1>/dev/null; + then + # skip brackets + find "$git_root" -type f \ + -name '*.dart' \ + -exec \ + grep -nIHE \ + -e $syntax_bracket \ + {} \; \ + | cut_and_join \ + >> $adjustments_file \ + || echo '' + fi + + if echo "$network" | grep -m1 '.php$' 1>/dev/null; + then + # skip empty lines, comments, and brackets + find "$git_root" -not -path "*/vendor/*" \ + -type f \ + -name '*.php' \ + -exec \ + grep -nIHE \ + -e $syntax_list \ + -e $syntax_bracket \ + -e '^[[:space:]]*\);[[:space:]]*(//.*)?$' \ + {} \; \ + | cut_and_join \ + >> $adjustments_file \ + || echo '' + fi + + if echo "$network" | grep -m1 '\(.cpp\|.h\|.cxx\|.c\|.hpp\|.m\)$' 1>/dev/null; + then + # skip brackets + find "$git_root" -type f \ + $skip_dirs \ + \( \ + -name '*.h' \ + -or -name '*.cpp' \ + -or -name '*.cxx' \ + -or -name '*.m' \ + -or -name '*.c' \ + -or -name '*.hpp' \ + \) -exec \ + grep -nIHE \ + -e $empty_line \ + -e $syntax_bracket \ + -e '// LCOV_EXCL' \ + {} \; \ + | cut_and_join \ + >> $adjustments_file \ + || echo '' + + # skip brackets + find "$git_root" -type f \ + $skip_dirs \ + \( \ + -name '*.h' \ + -or -name '*.cpp' \ + -or -name '*.cxx' \ + -or -name '*.m' \ + -or -name '*.c' \ + -or -name '*.hpp' \ + \) -exec \ + grep -nIH '// LCOV_EXCL' \ + {} \; \ + >> $adjustments_file \ + || echo '' + + fi + + found=$(cat $adjustments_file | tr -d ' ') + + if [ "$found" != "" ]; + then + say " ${g}+${x} Found adjustments" + echo "# path=fixes" >> $upload_file + cat $adjustments_file >> $upload_file + echo "<<<<<< EOF" >> $upload_file + rm -rf $adjustments_file + else + say " ${e}->${x} No adjustments found" + fi +fi + +if [ "$url_o" != "" ]; +then + url="$url_o" +fi + +if [ "$dump" != "0" ]; +then + # trim whitespace from query + say " ${e}->${x} Dumping upload file (no upload)" + echo "$url/upload/v4?$(echo "package=bash-$VERSION&token=$token&$query" | tr -d ' ')" + cat $upload_file +else + + say "${e}==>${x} Gzipping contents" + gzip -nf9 $upload_file + + query=$(echo "${query}" | tr -d ' ') + say "${e}==>${x} Uploading reports" + say " ${e}url:${x} $url" + say " ${e}query:${x} $query" + + # now add token to query + query=$(echo "package=bash-$VERSION&token=$token&$query" | tr -d ' ') + + if [ "$ft_s3" = "1" ]; + then + i="0" + while [ $i -lt 4 ] + do + i=$[$i+1] + say " ${e}->${x} Pinging Codecov" + res=$(curl $curl_s -X POST $curlargs $cacert \ + -H 'X-Reduced-Redundancy: false' \ + -H 'X-Content-Type: application/x-gzip' \ + "$url/upload/v4?$query" || true) + # a good replay is "https://codecov.io" + "\n" + "https://codecov.s3.amazonaws.com/..." + status=$(echo "$res" | head -1 | grep 'HTTP ' | cut -d' ' -f2) + if [ "$status" = "" ]; + then + s3target=$(echo "$res" | sed -n 2p) + say " ${e}->${x} Uploading" + s3=$(curl $curl_s -fiX PUT $curlawsargs \ + --data-binary @$upload_file.gz \ + -H 'Content-Type: application/x-gzip' \ + -H 'Content-Encoding: gzip' \ + -H 'x-amz-acl: public-read' \ + "$s3target" || true) + if [ "$s3" != "" ]; + then + say " ${g}->${x} View reports at ${b}$(echo "$res" | sed -n 1p)${x}" + exit 0 + else + say " ${r}X>${x} Failed to upload" + fi + elif [ "$status" = "400" ]; + then + # 400 Error + say "${g}${res}${x}" + exit ${exit_with} + fi + say " ${e}->${x} Sleeping for 30s and trying again..." + sleep 30 + done + fi + + say " ${e}->${x} Uploading to Codecov" + i="0" + while [ $i -lt 4 ] + do + i=$[$i+1] + + res=$(curl $curl_s -X POST $curlargs $cacert \ + --data-binary @$upload_file.gz \ + -H 'Content-Type: text/plain' \ + -H 'Content-Encoding: gzip' \ + -H 'X-Content-Encoding: gzip' \ + -H 'Accept: text/plain' \ + "$url/upload/v2?$query" || echo 'HTTP 500') + # HTTP 200 + # http://.... + status=$(echo "$res" | head -1 | cut -d' ' -f2) + if [ "$status" = "" ]; + then + say " View reports at ${b}$(echo "$res" | head -2 | tail -1)${x}" + exit 0 + + elif [ "${status:0:1}" = "5" ]; + then + say " ${e}->${x} Sleeping for 30s and trying again..." + sleep 30 + + else + say " ${g}${res}${x}" + exit 0 + exit ${exit_with} + fi + + done + + say " ${r}X> Failed to upload coverage reports${x}" +fi + +exit ${exit_with} diff --git a/scripts/config/dev.yml b/scripts/config/dev.yml new file mode 100644 index 0000000..1f9610a --- /dev/null +++ b/scripts/config/dev.yml @@ -0,0 +1,26 @@ +variants: + php-startup: + bcmath: + bz2: + calendar: + cli: + ctype: + dom: + fileinfo: + filter: + ipc: + json: + mbregex: + mbstring: + mhash: + mcrypt: + gd: + - --with-libdir=lib/x86_64-linux-gnu + - --with-gd=shared + - --enable-gd-natf + - --with-jpeg-dir=/usr + - --with-png-dir=/usr +extensions: + php-startup: + xhprof: latest + xdebug: stable diff --git a/scripts/fetch.php b/scripts/fetch.php new file mode 100755 index 0000000..23c46b9 --- /dev/null +++ b/scripts/fetch.php @@ -0,0 +1,44 @@ +#!/usr/bin/env php + 'https://github.com/tpunt/pht', + // 'php-ds' => 'https://github.com/php-ds/extension.git', + // 'phactor' => 'https://github.com/tpunt/phactor', + // 'phpredis' => 'https://github.com/phpredis/phpredis', + // 'yaf' => 'https://github.com/laruence/yaf.git', + // 'yar' => 'https://github.com/laruence/yar.git', + // 'yac' => 'https://github.com/laruence/yac.git', + // 'yaconf' => 'https://github.com/laruence/yaconf.git', + // 'yaf' => 'https://github.com/laruence/yaf.git', + // 'xhprof' => 'https://github.com/phacility/xhprof', + 'msgpack-php' => 'https://github.com/msgpack/msgpack-php', + // 'phptrace' => 'https://github.com/Qihoo360/phptrace', + // 'php-memcached' => 'https://github.com/php-memcached-dev/php-memcached', + // 'swoole' => 'https://github.com/swoole/swoole-src', + // 'async-ext' => 'https://github.com/swoole/async-ext.git', + // 'pthreads' => 'https://github.com/krakjoe/pthreads', + // 'SeasLog' => 'https://github.com/SeasX/SeasLog', + // 'phpdbg' => 'https://github.com/krakjoe/phpdbg', + // 'php-rdkafka' => 'https://github.com/arnaud-lb/php-rdkafka', + // 'runkit' => 'https://github.com/zenovich/runkit', + // 'php-git' => 'https://github.com/libgit2/php-git', + // 'AOP' => 'https://github.com/AOP-PHP/AOP', + // 'xdeubg' => 'https://github.com/xdebug/xdebug.git', + // 'apcu' => 'https://github.com/krakjoe/apcu.git', + // 'swoole-pgsql' => 'git@github.com:swoole/ext-postgresql.git', +]; + +// PHPBREW_ROOT/build/php-7.1.17/ext/ + +$path = sprintf('%s/build/%s/ext', $_SERVER['PHPBREW_ROOT'], $_SERVER['PHPBREW_PHP']); + +foreach ($data as $key => $value) { + if (file_exists("{$path}/{$key}")) { + echo "[INFO] 有了 {$path}/{$key}", PHP_EOL; + continue; + } + echo '[INFO] clone repo:[', $key, ']', PHP_EOL; + echo '[INFO] path:[', $path, ']', PHP_EOL; + `cd $path && git clone $value $key && cd -`; +} diff --git a/scripts/gdb.sh b/scripts/gdb.sh new file mode 100755 index 0000000..657a6ef --- /dev/null +++ b/scripts/gdb.sh @@ -0,0 +1,26 @@ +# #!/usr/bin/env bash + +# set -e +# set -x + +# source your_php_src_path/.gdbinit +# gdb php -f startup.php + +# [[ ! -e ~/.gdbinit ]]&& cp ~/.gdbinit ~/.gdbinit.bak + +# ln -sf $PHPBREW_HOME/build/$PHPBREW_PHP/.gdbinit ~/.gdbinit + +# ulimit -c unlimited + +# MINBABY_TEST_EXT=1 php -f startup.php + +# ulimit -c unlimited + +# gdb -c core php + +# git clone https://github.com/snare/voltron +sudo apt install gdb -f +mkdir -p ~/code/repos/ +git clone https://github.com/longld/peda.git ~/code/repos/peda +echo "source ~/code/repos/peda/peda.py" >> ~/.gdbinit +echo "DONE! debug your program with gdb and enjoy" \ No newline at end of file diff --git a/scripts/init-development.sh b/scripts/init-development.sh new file mode 100755 index 0000000..b278228 --- /dev/null +++ b/scripts/init-development.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash + +set -e -x -u + +export PATH=/usr/bin:$PATH + +export PHP_VERSION=php-7.2.34 + +export SYSTEM_NAME=`uname` + +export SCRIPT_DIR=`dirname $0` + +LIB_BZ2= +LIB_ZIP= +LIB_SSL= +if [ "$SYSTEM_NAME" == "Darwin" ] ;then + LIB_BZ2='=/usr/local/opt/bzip2/' + LIB_ZIP='=/usr/local/opt/zlib/' + LIB_SSL='=/usr/local/opt/openssl/' +fi + +[ "$SYSTEM_NAME" == "Darwin" ] && LIB_BZ2='=/usr/local/opt/bzip2/' +[ "$SYSTEM_NAME" == "Darwin" ] && LIB_ZIP='=/usr/local/opt/zlib/' +[ "$SYSTEM_NAME" == "Darwin" ] && LIB_SSL='=/usr/local/opt/openssl/' + +if [ ! -x "$(command -v icu-config)" ]; then + sudo cp $SCRIPT_DIR/resources/icu-config /usr/bin/icu-config +fi + +if [ "$SYSTEM_NAME" == "Linux" ]; then + sudo apt install -y \ + libxml2-dev \ + libssh-dev \ + libbz2-dev \ + libcurl4-openssl-dev \ + libmcrypt-dev \ + libreadline-dev \ + libxslt1-dev \ + autoconf \ + valgrind +fi + +phpbrew --debug install \ + -j $(nproc) \ + $PHP_VERSION \ + +default \ + +bz2$LIB_BZ2 \ + +zlib$LIB_ZIP \ + +mb \ + +openssl$LIB_SSL \ + +intl \ + +mysql \ + +pdo \ + +xml + +echo $PHP_VERSION + +phpbrew switch $PHP_VERSION diff --git a/scripts/prepare.sh b/scripts/prepare.sh new file mode 100755 index 0000000..262d843 --- /dev/null +++ b/scripts/prepare.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -e + +echo "prepare..." +phpize && ./configure --enable-debug diff --git a/scripts/resources/icu-config b/scripts/resources/icu-config new file mode 100755 index 0000000..077d64b --- /dev/null +++ b/scripts/resources/icu-config @@ -0,0 +1,765 @@ +#!/bin/sh +## -*-sh-*- +#set -x +# BEGIN of icu-config-top +# Copyright (C) 2016 and later: Unicode, Inc. and others. +# License & terms of use: http://www.unicode.org/copyright.html +#****************************************************************************** +# Copyright (C) 1999-2013, International Business Machines +# Corporation and others. All Rights Reserved. +#****************************************************************************** +# This script is designed to aid configuration of ICU. +# rpath links a library search path right into the binaries. +# +# Note: it's preferred to use the .pc files rather than icu-config. +# + +### END of icu-config-top + +## Zero out prefix. +execprefix= +prefix= + + +loaddefs() +{ + +# Following from icu/icu4c/source/config/Makefile.inc +# Copyright (C) 2016 and later: Unicode, Inc. and others. +# License & terms of use: http://www.unicode.org/copyright.html +## -*-makefile-*- +#****************************************************************************** +# Copyright (C) 1999-2014, International Business Machines +# Corporation and others. All Rights Reserved. +#****************************************************************************** +# This Makefile.inc is designed to be included into projects which make use +# of the ICU. + +# CONTENTS OF THIS FILE +# 1). Base configuration information and linkage +# 2). Variables giving access to ICU tools +# 3). Host information +# 4). Compiler flags and settings +# 5). Data Packaging directives +# 6). Include of platform make fragment (mh-* file) + +################################################################## +# +# *1* base configuration information and linkage +# +################################################################## +# The PREFIX is the base of where ICU is installed. +# Inside this directory you should find bin, lib, include/unicode, +# etc. If ICU is not installed in this directory, you must change the +# following line. There should exist ${prefix}/include/unicode/utypes.h +# for example. +default_prefix="/usr" +if [ "x${prefix}" = "x" ]; then prefix="$default_prefix"; fi +exec_prefix="${prefix}" +libdir="${prefix}/lib/x86_64-linux-gnu" +libexecdir="${prefix}/lib/icu" +bindir="${exec_prefix}/bin" +datarootdir="${prefix}/share" +datadir="${datarootdir}" +sbindir="${exec_prefix}/sbin" + +# about the ICU version +VERSION="66.1" +UNICODE_VERSION="13.0" + +# The prefix for ICU libraries, normally 'icu' +ICUPREFIX="icu" +PACKAGE="icu" +LIBICU="lib${ICUPREFIX}" + +# Static library prefix and file extension +STATIC_PREFIX="s" +LIBSICU="lib${STATIC_PREFIX}${ICUPREFIX}" +A="a" + +# Suffix at the end of libraries. Usually empty. +ICULIBSUFFIX="" +# ICULIBSUFFIX_VERSION is non-empty if it is to contain a library +# version. For example, if it is 21, it means libraries are named +# libicuuc21.so for example. + +# rpath links a library search path right into the binaries. +## mh-files MUST NOT override RPATHLDFLAGS unless they provide +## equivalent '#SH#' lines for icu-config fixup +default_ENABLE_RPATH="NO" +if [ "x${ENABLE_RPATH}" = "x" ]; then ENABLE_RPATH="$default_ENABLE_RPATH"; fi +RPATHLDFLAGS="${LD_RPATH}${LD_RPATH_PRE}${libdir}" + +# icu-config version of above 'if': +case "x$ENABLE_RPATH" in +x[yY]*) + ENABLE_RPATH=YES + RPATHLDFLAGS="${LD_RPATH}${LD_RPATH_PRE}${libdir}" + ;; + +x[nN]*) + ENABLE_RPATH=NO + RPATHLDFLAGS="" + ;; + +x) + ENABLE_RPATH=NO + RPATHLDFLAGS="" + ;; + +*) + echo $0: Unknown --enable-rpath value ${ENABLE_RPATH} 1>&2 + exit 3 + ;; +esac + +# Name flexibility for the library naming scheme. Any modifications should +# be made in the mh- file for the specific platform. +DATA_STUBNAME="data" +COMMON_STUBNAME="uc" +I18N_STUBNAME="i18n" +LAYOUTEX_STUBNAME="lx" +IO_STUBNAME="io" +TOOLUTIL_STUBNAME="tu" +CTESTFW_STUBNAME="test" + + +### To link your application with ICU: +# 1. use LDFLAGS, CFLAGS, etc from above +# 2. link with ${ICULIBS} +# 3. optionally, add one or more of: +# - ${ICULIBS_I18N} - i18n library, formatting, etc. +# - ${ICULIBS_ICUIO} - ICU stdio equivalent library + +ICULIBS_COMMON="-l${ICUPREFIX}uc${ICULIBSUFFIX}${ICULIBSUFFIX_VERSION}" +ICULIBS_DATA="-l${ICUPREFIX}${DATA_STUBNAME}${ICULIBSUFFIX}${ICULIBSUFFIX_VERSION}" +ICULIBS_I18N="-l${ICUPREFIX}${I18N_STUBNAME}${ICULIBSUFFIX}${ICULIBSUFFIX_VERSION}" +ICULIBS_TOOLUTIL="-l${ICUPREFIX}tu${ICULIBSUFFIX}${ICULIBSUFFIX_VERSION}" +ICULIBS_CTESTFW="-l${ICUPREFIX}ctestfw${ICULIBSUFFIX}${ICULIBSUFFIX_VERSION}" +ICULIBS_ICUIO="-l${ICUPREFIX}io${ICULIBSUFFIX}${ICULIBSUFFIX_VERSION}" +ICULIBS_OBSOLETE="-l${ICUPREFIX}obsolete${ICULIBSUFFIX}${ICULIBSUFFIX_VERSION}" +ICULIBS_LAYOUTEX="-l${ICUPREFIX}lx${ICULIBSUFFIX}${ICULIBSUFFIX_VERSION}" +ICULIBS_BASE="-L${libdir}" + +# for icu-config to test with +ICULIBS_COMMON_LIB_NAME="${LIBICU}${COMMON_STUBNAME}${ICULIBSUFFIX}${ICULIBSUFFIX_VERSION}.${SO}" +ICULIBS_COMMON_LIB_NAME_A="${LIBICU}${COMMON_STUBNAME}${ICULIBSUFFIX}.${A}" + +# ICULIBS is the set of libraries your application should link +# with usually. Many applications will want to add ${ICULIBS_I18N} as well. +ICULIBS="${ICULIBS_BASE} ${ICULIBS_I18N} ${ICULIBS_COMMON} ${ICULIBS_DATA} " + +# Proper echo newline handling is needed in icu-config +ECHO_N="-n" +ECHO_C="" +# Not currently being used but good to have for proper tab handling +ECHO_T="" + +################################################################## +# +# *2* access to ICU tools +# +################################################################## +# Environment variable to set a runtime search path +# (Overridden when necessary in -mh files) +LDLIBRARYPATH_ENVVAR="LD_LIBRARY_PATH" + +# Versioned target for a shared library +## FINAL_SO_TARGET = ${SO_TARGET}.${SO_TARGET_VERSION} +## MIDDLE_SO_TARGET = ${SO_TARGET}.${SO_TARGET_VERSION_MAJOR} + +# Access to important ICU tools. +# Use as follows: ${INVOKE} ${GENRB} arguments .. +INVOKE="${LDLIBRARYPATH_ENVVAR}=${libdir}:$$${LDLIBRARYPATH_ENVVAR} ${LEAK_CHECKER}" +GENCCODE="${sbindir}/genccode" +ICUPKG="${sbindir}/icupkg" +GENCMN="${sbindir}/gencmn" +GENRB="${bindir}/genrb" +PKGDATA="${bindir}/pkgdata" + +# moved here because of dependencies +pkgdatadir="${datadir}/${PACKAGE}${ICULIBSUFFIX}/${VERSION}" +pkglibdir="${libdir}/${PACKAGE}${ICULIBSUFFIX}/${VERSION}" + +################################################################## +# +# *3* Information about the host +# +################################################################## + +# Information about the host that 'configure' was run on. +host="x86_64-pc-linux-gnu" +host_alias="" +host_cpu="x86_64" +host_vendor="pc" +host_os="linux-gnu" +# Our platform canonical name (as determined by configure) +# this is a #define value (i.e. U_XXXX or XXXX) +platform="U_LINUX" + +################################################################## +# +# *4* compiler flags and misc. options +# +################################################################## +AR="ar" +# initial tab keeps it out of the shell version. +ARFLAGS=" ${ARFLAGS}" +CC="gcc" +CPP="gcc -E" +CFLAGS="" +CPPFLAGS="-I${prefix}/include" +CXXFLAGS="-std=c++11" +CXX="g++" +DEFAULT_MODE="dll" +DEFS="-DPACKAGE_NAME=\"ICU\" -DPACKAGE_TARNAME=\"International\ Components\ for\ Unicode\" -DPACKAGE_VERSION=\"66.1\" -DPACKAGE_STRING=\"ICU\ 66.1\" -DPACKAGE_BUGREPORT=\"http://icu-project.org/bugs\" -DPACKAGE_URL=\"http://icu-project.org\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DSIZEOF_VOID_P=8 -DHAVE_LIBM=1 -DHAVE_ELF_H=1 -DHAVE_DLFCN_H=1 -DHAVE_DLOPEN=1 -DHAVE_GETTIMEOFDAY=1 -DHAVE_LIBPTHREAD=1 -DHAVE_INTTYPES_H=1 -DHAVE_DIRENT_H=1 -DHAVE_WCHAR_H=1 -DSIZEOF_WCHAR_T=4 " +# use a consistent INSTALL +INSTALL="${SHELL} ${pkgdatadir}/install-sh -c" +INSTALL_DATA="${INSTALL} -m 644" +INSTALL_PROGRAM="${INSTALL}" +INSTALL_SCRIPT="${INSTALL}" +LDFLAGS="${RPATHLDFLAGS}" +LIBS="-lpthread -ldl -lm " +LIB_M="" +LIB_VERSION="66.1" +LIB_VERSION_MAJOR="66" +MKINSTALLDIRS="${SHELL} ${pkgdatadir}/mkinstalldirs" +RANLIB="ranlib" +RMV="rm -rf" +SHELL="/bin/bash" +SHLIB_c="${CC} ${DEFS} ${CPPFLAGS} ${CFLAGS} ${LDFLAGS} -shared" +SHLIB_cc="${CXX} ${DEFS} ${CPPFLAGS} ${CXXFLAGS} ${LDFLAGS} -shared" +U_IS_BIG_ENDIAN="0" +includedir="${prefix}/include/x86_64-linux-gnu" +infodir="${prefix}/share/info" +localstatedir="/var" +mandir="${prefix}/share/man" +oldincludedir="/usr/include" +program_transform_name="s,x,x," +sharedstatedir="${prefix}/com" +sysconfdir="/etc" +INSTALL_L="${INSTALL_DATA}" + +# for derivative builds - don't bother with VERBOSE/NONVERBOSE SILENT_COMPILE + +################################################################## +# +# *5* packaging options and directories +# +################################################################## + +# The basename of the ICU data file (i.e. icudt21b ) +ICUDATA_CHAR="l" +ICUDATA_NAME="icudt66l" + +# Defaults for pkgdata's mode and directories +# The default data dir changes depending on what packaging mode +# is being used +if [ "x$PKGDATA_MODE" = "x" ]; +then +PKGDATA_MODE="dll" +fi + +case "$PKGDATA_MODE" in +common) +ICUDATA_DIR="${pkgdatadir}" +ICUPKGDATA_DIR="${ICUDATA_DIR}" +;; +dll) +ICUDATA_DIR="${pkgdatadir}" +ICUPKGDATA_DIR="${libdir}" +;; +*) +ICUDATA_DIR="${pkgdatadir}" +ICUPKGDATA_DIR="${ICUDATA_DIR}" +;; + +esac + +GENCCODE_ASSEMBLY="-a gcc" + +################################################################## +# +# *6* Inclusion of platform make fragment (mh-* file) +# +################################################################## +# The mh- file ("make fragment") for the platform is included here. +# It may override the above settings. +# It is put last so that the mh-file can override anything. +# The selfcheck is just a sanity check that this makefile is +# parseable. The mh fragment is only included if this does not occur. + +# Following from ./config/mh-linux +## -*-makefile-*- +## Copyright (C) 2016 and later: Unicode, Inc. and others. +## License & terms of use: http://www.unicode.org/copyright.html +## Linux-specific setup +## Copyright (c) 1999-2013, International Business Machines Corporation and +## others. All Rights Reserved. + +## Commands to generate dependency files +GEN_DEPS_c="${CC} -E -MM ${DEFS} ${CPPFLAGS}" +GEN_DEPS_cc="${CXX} -E -MM ${DEFS} ${CPPFLAGS} ${CXXFLAGS}" + +## Flags for position independent code +SHAREDLIBCFLAGS="-fPIC" +SHAREDLIBCXXFLAGS="-fPIC" +SHAREDLIBCPPFLAGS="-DPIC" + +## Additional flags when building libraries and with threads +THREADSCPPFLAGS="-D_REENTRANT" +LIBCPPFLAGS="" + +## Compiler switch to embed a runtime search path +LD_RPATH="-Wl,-zorigin,-rpath,\$\$ORIGIN " +LD_RPATH_PRE="-Wl,-rpath," + +## These are the library specific LDFLAGS +LDFLAGSICUDT="-nodefaultlibs -nostdlib" + +## Compiler switch to embed a library name +# The initial tab in the next line is to prevent icu-config from reading it. +# We can't depend on MIDDLE_SO_TARGET being set. +LD_SONAME= + +## Shared library options +LD_SOOPTIONS="-Wl,-Bsymbolic" + +## Shared object suffix +SO="so" +## Non-shared intermediate object suffix +STATIC_O="ao" + +## Compilation rules + +## Dependency rules + +## Versioned libraries rules + +## Bind internal references + +# LDflags that pkgdata will use +BIR_LDFLAGS="-Wl,-Bsymbolic" + +# Dependencies [i.e. map files] for the final library +BIR_DEPS="" + +## Remove shared library 's' +STATIC_PREFIX_WHEN_USED="" +STATIC_PREFIX="" + +## End Linux-specific setup + +## -*-sh-*- +## BEGIN of icu-config-bottom. +## Copyright (C) 2016 and later: Unicode, Inc. and others. +## License & terms of use: http://www.unicode.org/copyright.html +## Copyright (c) 2002-2013, International Business Machines Corporation and +## others. All Rights Reserved. + +ICUUC_FILE="${libdir}/${ICULIBS_COMMON_LIB_NAME}" +ICUUC_FILE_A="${libdir}/${ICULIBS_COMMON_LIB_NAME_A}" + +# echo ENABLE RPATH $ENABLE_RPATH and RPATHLDFLAGS=${RPATH_LDFLAGS} +if [ "x$PKGDATA_MODE" = "x" ]; then + PKGDATA_MODE=dll +fi + +} + +## The actual code of icu-config goes here. + +ME=`basename "$0"` + +allflags() +{ + echo " --noverify Don't verify that ICU is actually installed." + echo " --bindir Print binary directory path (bin)" + echo " --cc Print C compiler used [CC]" + echo " --cflags Print C compiler flags [CFLAGS]" + echo " --cflags-dynamic Print additional C flags for" + echo " building shared libraries." + echo " --cppflags Print C Preprocessor flags [CPPFLAGS]" + echo " --cppflags-dynamic Print additional C Preprocessor flags for" + echo " building shared libraries." + echo " --cppflags-searchpath Print only -I include directives (-Iinclude)" + echo " --cxx Print C++ compiler used [CXX]" + echo " --cxxflags Print C++ compiler flags [CXXFLAGS]" + echo " --cxxflags-dynamic Print additional C++ flags for" + echo " building shared libraries." + echo " --detect-prefix Attempt to detect prefix based on PATH" + echo " --exec-prefix Print prefix for executables (/bin)" + echo " --exists Return with 0 status if ICU exists else fail" + echo " --help, -?, --usage Print this message" + echo " --icudata Print shortname of ICU data file (icudt21l)" + echo " --icudata-install-dir Print path to install data to - use as --install option to pkgdata(1)" + echo " --icudata-mode Print default ICU pkgdata mode (dll) - use as --mode option to pkgdata(1)." + echo " --icudatadir Print path to packaged archive data. Can set as [ICU_DATA]" + echo " --invoke Print commands to invoke an ICU program" + echo " --invoke= Print commands to invoke an ICU program named (ex: genrb)" + echo " --ldflags Print -L search path and -l libraries to link with ICU [LDFLAGS]. This is for the data, uc (common), and i18n libraries only. " + echo " --ldflags-libsonly Same as --ldflags, but only the -l directives" + echo " --ldflags-searchpath Print only -L (search path) directive" + echo " --ldflags-system Print only system libs ICU links with (-lpthread, -lm)" + echo " --ldflags-icuio Print ICU icuio link directive. Use in addition to --ldflags " + echo " --ldflags-obsolete Print ICU obsolete link directive. Use in addition to --ldflags. (requires icuapps/obsolete to be built and installed.) " + echo " --mandir Print manpage (man) path" + echo " --prefix Print PREFIX to icu install (/usr/local)" + echo " --prefix=XXX Set prefix to XXX for remainder of command" + echo " --sbindir Print system binary path (sbin) " + echo " --shared-datadir Print shared data (share) path. This is NOT the ICU data dir." + echo " --shlib-c Print the command to compile and build C shared libraries with ICU" + echo " --shlib-cc Print the command to compile and build C++ shared libraries with ICU" + echo " --sysconfdir Print system config (etc) path" + echo " --unicode-version Print version of Unicode data used in ICU ($UNICODE_VERSION)" + echo " --version Print ICU version ($VERSION)" + echo " --incfile Print path to Makefile.inc" + echo " --incpkgdatafile Print path to pkgdata.inc (for -O option of pkgdata)" + echo " --install Print path to install-sh" + echo " --mkinstalldirs Print path to mkinstalldirs" +} + +## Print the normal usage message +shortusage() +{ + echo "usage: ${ME} " `allflags | cut -c-25 | sed -e 's%.*%[ & ]%'` +} + + +usage() +{ + echo "${ME}: icu-config: ICU configuration helper script" + echo + echo "The most commonly used options will be --cflags, --cxxflags, --cppflags, and --ldflags." + echo 'Example (in make): CPFLAGS=$(shell icu-config --cppflags)' + echo ' LDFLAGS=$(shell icu-config --ldflags)' + echo " (etc).." + echo + echo "Usage:" + allflags + + echo + echo " [Brackets] show MAKE variable equivalents, (parenthesis) show example output" + echo + echo "Copyright (c) 2002-2013, International Business Machines Corporation and others. All Rights Reserved." + echo + echo "NOTE: Please consider using the pkg-config (.pc) files instead of icu-config." + echo " See: " +} + +## Check the sanity of current variables +sanity() +{ + if [ ! -f "${ICUUC_FILE}" -a ! -f "${ICUUC_FILE_A}" ] && [ ${IGNORE_ICUUC_FILE_CHECK} = "no" ] && [ ${SANITY} = "sane" ]; + then + echo "### $ME: Can't find ${ICUUC_FILE} - ICU prefix is wrong." 1>&2 + echo "### Try the --prefix= option " 1>&2 + echo "### or --detect-prefix" 1>&2 + echo "### (If you want to disable this check, use the --noverify option)" 1>&2 + echo "### $ME: Exitting." 1>&2 + exit 2 + fi +} + +## Main starts here. + +if [ $# -lt 1 ]; then + shortusage + exit 1 +fi + +# For certain options (e.g. --detect-prefix) don't check for icuuc library file. + +IGNORE_ICUUC_FILE_CHECK="no"; + +SANITY="sane" + +case "$1" in +--noverify) + SANITY="nosanity" + shift + ;; +esac + +case "$1" in +*prefix*) + IGNORE_ICUUC_FILE_CHECK="yes" + ;; +esac + +# Load our variables from autoconf +# ALWAYS load twice because of dependencies +loaddefs +loaddefs + +if [ $# -gt 0 -a $1 = "--selfcheck" ]; +then + echo "passed" + exit + # EXIT for self check +fi + +sanity + +while [ $# -gt 0 ]; +do + arg="$1" + var=`echo $arg | sed -e 's/^[^=]*=//'` +# echo "### processing $arg" 1>&2 + case "$arg" in + + # undocumented. + --debug) + set -x + ;; + + --noverify) + echo "### $ME: Error: --noverify must be the first argument." 1>&2 + exit 1 + ;; + + --so) + echo $SO + ;; + + --bindir) + echo $bindir + ;; + + --libdir) + echo $libdir + ;; + + --exists) + sanity + ;; + + --sbindir) + echo $sbindir + ;; + + --mkinstalldirs) + echo ${MKINSTALLDIRS} + ;; + + --install) + echo ${INSTALL} + ;; + + --invoke=*) + QUOT="\"" + CMD="${var}" + + # If it's not a locally executable command (1st choice) then + # search for it in the ICU directories. + if [ ! -x ${CMD} ]; then + if [ -x ${bindir}/${var} ]; then + CMD="${bindir}/${var}" + fi + if [ -x ${sbindir}/${var} ]; then + CMD="${sbindir}/${var}" + fi + fi + + echo "env ${QUOT}${LDLIBRARYPATH_ENVVAR}=${libdir}:"'${'"${LDLIBRARYPATH_ENVVAR}"'}'${QUOT} ${CMD} + ;; + + --invoke) + QUOT="\"" + echo "env ${QUOT}${LDLIBRARYPATH_ENVVAR}=${libdir}:"'${'"${LDLIBRARYPATH_ENVVAR}"'}'${QUOT} + ;; + + --cflags) + echo $ECHO_N "${CFLAGS} ${ECHO_C}" + ;; + + --cc) + echo $ECHO_N "${CC} ${ECHO_C}" + ;; + + --cxx) + echo $ECHO_N "${CXX} ${ECHO_C}" + ;; + + --cxxflags) + echo $ECHO_N "${CXXFLAGS} ${ECHO_C}" + ;; + + --cppflags) + # Don't echo the -I. - it's unneeded. + echo $ECHO_N "${CPPFLAGS} ${ECHO_C}" | sed -e 's/-I. //' + ;; + + --cppflags-searchpath) + echo $ECHO_N "-I${prefix}/include ${ECHO_C}" + ;; + + --cppflags-dynamic) + echo $ECHO_N "${SHAREDLIBCPPFLAGS} ${ECHO_C}" + ;; + + --cxxflags-dynamic) + echo $ECHO_N "${SHAREDLIBCXXFLAGS} ${ECHO_C}" + ;; + + --cflags-dynamic) + echo $ECHO_N "${SHAREDLIBCFLAGS} ${ECHO_C}" + ;; + + --ldflags-system) + echo $ECHO_N "${LIBS} ${ECHO_C}" + ;; + + --ldflags) + echo $ECHO_N "${LDFLAGS} ${ICULIBS} ${ECHO_C}" +# $RPATH_LDFLAGS + ;; + + --ldflags-libsonly) + echo $ECHO_N "${ICULIBS_I18N} ${ICULIBS_COMMON} ${ICULIBS_DATA} ${ECHO_C}" + ;; + + --ldflags-icuio) + echo $ECHO_N " ${ICULIBS_ICUIO} ${ECHO_C}" + ;; + + --ldflags-obsolete) + echo $ECHO_N "${ICULIBS_OBSOLETE} ${ECHO_C}" + ;; + + --ldflags-toolutil) + echo $ECHO_N " ${ICULIBS_TOOLUTIL} ${ECHO_C}" + ;; + + --ldflags-layout) + echo ${ME}: ERROR: the old layout engine has been removed. use HarfBuzz. + exit 1 + ;; + + --ldflags-searchpath) + echo $ECHO_N "-L${libdir} ${ECHO_C}" + ;; + + --detect-prefix) + HERE=`echo $0 | sed -e "s/$ME//g"` + if [ -f "${HERE}/../lib/${ICULIBS_COMMON_LIB_NAME}" -o -f "${HERE}/../lib/${ICULIBS_COMMON_LIB_NAME_A}" ]; then + prefix="${HERE}/.." + echo "## Using --prefix=${prefix}" 1>&2 + fi + loaddefs + loaddefs + ;; + + --exec-prefix) + echo $exec_prefix + ;; + + --prefix) + echo $prefix + ;; + + --prefix=*) + prefix=$var + loaddefs + loaddefs + ;; + + --sysconfdir) + echo $sysconfdir + ;; + + --mandir) + echo $mandir + ;; + + --shared-datadir) + echo $ECHO_N "${datadir} ${ECHO_C}" + ;; + + --incfile) + echo $ECHO_N "${pkglibdir}/Makefile.inc ${ECHO_C}" + ;; + + --incpkgdatafile) + echo $ECHO_N "${pkglibdir}/pkgdata.inc ${ECHO_C}" + ;; + + --icudata) + echo $ECHO_N "${ICUDATA_NAME} ${ECHO_C}" + ;; + + --icudata-mode) + echo $ECHO_N "${PKGDATA_MODE} ${ECHO_C}" + ;; + + --icudata-install-dir) + echo $ECHO_N "${ICUPKGDATA_DIR} ${ECHO_C}" + ;; + + --icudatadir) + echo $ECHO_N "${ICUDATA_DIR} ${ECHO_C}" + ;; + + --shlib-c) + echo $ECHO_N "${SHLIB_c} ${ECHO_C}" + ;; + + --shlib-cc) + echo $ECHO_N "${SHLIB_cc} ${ECHO_C}" + ;; + + --version) + echo $ECHO_N $VERSION + ;; + + --unicode-version) + echo $ECHO_N $UNICODE_VERSION + ;; + + --host) + echo $host + exit 0 + ;; + + --help) + usage + exit 0 + ;; + + --usage) + usage + exit 0 + ;; + +# --enable-rpath=*) +# ENABLE_RPATH=$var +# loaddefs +# ;; + + -?) + usage + exit 0 + ;; + + *) + echo ${ME}: ERROR Unknown Option $arg 1>&2 + echo 1>&2 + shortusage 1>&2 + echo "### $ME: Exitting." 1>&2 + exit 1; + ;; + esac + shift + + # Reset the ignore icuuc file check flag + if [ $IGNORE_ICUUC_FILE_CHECK = "yes" ]; then + IGNORE_ICUUC_FILE_CHECK="no" + sanity + fi +done +echo +# Check once before we quit (will check last used prefix) +sanity +## END of icu-config-bottom + +exit 0 \ No newline at end of file diff --git a/tools/test.sh b/scripts/test.sh similarity index 69% rename from tools/test.sh rename to scripts/test.sh index e041d0a..bbb2194 100755 --- a/tools/test.sh +++ b/scripts/test.sh @@ -1,16 +1,18 @@ #!/usr/bin/env bash set -e +set -x pwd # echo "disable ext startup.so" phpbrew ext disable startup +export MINBABY_TEST_EXT=0 ./vendor/bin/kahlan # echo "enable ext start.so" phpbrew ext enable startup +export MINBABY_TEST_EXT=1 ./vendor/bin/kahlan - - +export MINBABY_TEST_EXT=0 diff --git a/spec/Stringy/StringySpec.php b/spec/Stringy/StringySpec.php new file mode 100644 index 0000000..8816321 --- /dev/null +++ b/spec/Stringy/StringySpec.php @@ -0,0 +1,2103 @@ +class = 'Stringy'; + $this->className = __($this->class); + }); + + it('test construct', function () { + $stringy = _('Stringy', ['test test2 test3', 'UTF-8']); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe('test test2 test3'); + \expect($stringy->getEncoding())->toBe('UTF-8'); + }); + + it('test empty construct', function () { + $stringy = _('Stringy'); + + \expect($stringy instanceof $this->className)->toBe(true); + \expect((string) $stringy)->toBe(''); + }); + + it('test construct with array', function () { + $closure = function () { + (string) _($this->class, [[]]); + }; + \expect($closure)->toThrow(new \InvalidArgumentException('Passed value cannot be an array')); + }); + + it('test missing __toString', function () { + $closure = function () { + (string) _($this->class, [new \stdClass()]); + }; + + \expect($closure)->toThrow(new \InvalidArgumentException('Passed object must have a __toString method')); + }); + + it('test __toString', function () { + $data = [ + ['', null], + ['', false], + ['1', true], + ['-9', -9], + ['1.18', 1.18], + [' string ', ' string '], + ['❤', '❤'], + ]; + + foreach ($data as $v) { + list($key, $value) = $v; + \expect($key)->toBe((string) _($this->class, [$value])); + } + }); + + it('test create', function () { + $stringy = __('Stringy')::create('foo bar', 'UTF-8'); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect('foo bar')->toBe((string) $stringy); + \expect('UTF-8')->toBe($stringy->getEncoding()); + }); + + it('test chaining', function () { + $stringy = __('Stringy')::create('x y', 'UTF-8'); + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy->swapCase())->toBe('X Y'); + }); + + it('test count', function () { + $stringy = __('Stringy')::create('Fòô', 'UTF-8'); + \expect($stringy->count())->toBe(3); + \expect(count($stringy))->toBe(3); + }); + + it('test GetIterator', function () { + $stringy = __('Stringy')::create('Fòô Bàř', 'UTF-8'); + $valResult = []; + foreach ($stringy as $char) { + $valResult[] = $char; + } + $keyValResult = []; + foreach ($stringy as $pos => $char) { + $keyValResult[$pos] = $char; + } + + \expect($valResult)->toBe(['F', 'ò', 'ô', ' ', 'B', 'à', 'ř']); + \expect($keyValResult)->toBe(['F', 'ò', 'ô', ' ', 'B', 'à', 'ř']); + }); + + it('test offsetExists', function () { + $data = [ + [true, 0], + [true, 2], + [false, 3], + [true, -1], + [true, -3], + [false, -4], + ]; + + $stringy = __('Stringy')::create('fòô', 'UTF-8'); + + foreach ($data as $value) { + list($expected, $offset) = $value; + \expect($stringy->offsetExists($offset))->toBe($expected); + \expect(isset($stringy[$offset]))->toBe($expected); + } + }); + + it('test OffsetGet', function () { + $stringy = __('Stringy')::create('fòô', 'UTF-8'); + + \expect($stringy->offsetGet(0))->toBe('f'); + \expect($stringy->offsetGet(1))->toBe('ò'); + + \expect($stringy[2])->toBe('ô'); + }); + + it('test OffsetGet out of bounds', function () { + $stringy = __('Stringy')::create('fòô', 'UTF-8'); + $callable = function () use ($stringy) { + $test = $stringy[3]; + }; + \expect($callable)->toThrow(new \OutOfBoundsException('No character exists at the index')); + }); + + it('test OffsetSet', function () { + $stringy = __('Stringy')::create('fòô', 'UTF-8'); + $callable = function () use ($stringy) { + $stringy[1] = 'invalid'; + }; + \expect($callable)->toThrow(new \Exception('Stringy object is immutable, cannot modify char')); + }); + + it('test OffsetUnset', function () { + $stringy = __('Stringy')::create('fòô', 'UTF-8'); + $callable = function () use ($stringy) { + unset($stringy[1]); + }; + \expect($callable)->toThrow(new \Exception('Stringy object is immutable, cannot unset char')); + }); + + it('test IndexOf', function () { + $data = [ + [6, 'foo & bar', 'bar'], + [6, 'foo & bar', 'bar', 0], + [false, 'foo & bar', 'baz'], + [false, 'foo & bar', 'baz', 0], + [0, 'foo & bar & foo', 'foo', 0], + [12, 'foo & bar & foo', 'foo', 5], + [6, 'fòô & bàř', 'bàř', 0, 'UTF-8'], + [false, 'fòô & bàř', 'baz', 0, 'UTF-8'], + [0, 'fòô & bàř & fòô', 'fòô', 0, 'UTF-8'], + [12, 'fòô & bàř & fòô', 'fòô', 5, 'UTF-8'], + ]; + + foreach ($data as $value) { + @list($expectedd, $str, $subStr, $offset, $encoding) = $value; + $result = __('Stringy')::create($str, $encoding)->indexOf($subStr, $offset); + \expect($result)->toBe($expectedd); + } + }); + + it('test IndexOfLast', function () { + $data = [ + [6, 'foo & bar', 'bar'], + [6, 'foo & bar', 'bar', 0], + [false, 'foo & bar', 'baz'], + [false, 'foo & bar', 'baz', 0], + [12, 'foo & bar & foo', 'foo', 0], + [0, 'foo & bar & foo', 'foo', -5], + [6, 'fòô & bàř', 'bàř', 0, 'UTF-8'], + [false, 'fòô & bàř', 'baz', 0, 'UTF-8'], + [12, 'fòô & bàř & fòô', 'fòô', 0, 'UTF-8'], + [0, 'fòô & bàř & fòô', 'fòô', -5, 'UTF-8'], + ]; + + foreach ($data as $value) { + @list($expectedd, $str, $subStr, $offset, $encoding) = $value; + $result = __('Stringy')::create($str, $encoding)->indexOfLast($subStr, $offset); + \expect($result)->toBe($expectedd); + } + }); + + it('test append', function () { + $data = [ + ['foobar', 'foo', 'bar'], + ['fòôbàř', 'fòô', 'bàř', 'UTF-8'], + ]; + foreach ($data as $value) { + @list($expected, $str, $string, $encoding) = $value; + $result = __('Stringy')::create($str, $encoding)->append($string); + \expect($result)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $result)->toBe($expected); + } + }); + + it('test prepend', function () { + $data = [ + ['foobar', 'bar', 'foo'], + ['fòôbàř', 'bàř', 'fòô', 'UTF-8'], + ]; + foreach ($data as $value) { + @list($expected, $str, $string, $encoding) = $value; + $result = __('Stringy')::create($str, $encoding)->prepend($string); + \expect($result)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $result)->toBe($expected); + } + }); + + it('test chars', function () { + $data = [ + [[], ''], + [['T', 'e', 's', 't'], 'Test'], + [['F', 'ò', 'ô', ' ', 'B', 'à', 'ř'], 'Fòô Bàř', 'UTF-8'], + ]; + + foreach ($data as $value) { + @list($expected, $str, $encoding) = $value; + + $result = __('Stringy')::create($str, $encoding)->chars(); + \expect($result)->toBeA('array'); + + foreach ($result as $char) { + \expect($char)->toBeA('string'); + } + + \expect($result)->toBe($expected); + } + }); + + it('test lines', function () { + $data = [ + [[], ''], + [[''], "\r\n"], + [['foo', 'bar'], "foo\nbar"], + [['foo', 'bar'], "foo\rbar"], + [['foo', 'bar'], "foo\r\nbar"], + [['foo', '', 'bar'], "foo\r\n\r\nbar"], + [['foo', 'bar', ''], "foo\r\nbar\r\n"], + [['', 'foo', 'bar'], "\r\nfoo\r\nbar"], + [['fòô', 'bàř'], "fòô\nbàř", 'UTF-8'], + [['fòô', 'bàř'], "fòô\rbàř", 'UTF-8'], + [['fòô', 'bàř'], "fòô\n\rbàř", 'UTF-8'], + [['fòô', 'bàř'], "fòô\r\nbàř", 'UTF-8'], + [['fòô', '', 'bàř'], "fòô\r\n\r\nbàř", 'UTF-8'], + [['fòô', 'bàř', ''], "fòô\r\nbàř\r\n", 'UTF-8'], + [['', 'fòô', 'bàř'], "\r\nfòô\r\nbàř", 'UTF-8'], + ]; + + foreach ($data as $value) { + @list($expected, $str, $encoding) = $value; + + $result = __('Stringy')::create($str, $encoding)->lines(); + \expect($result)->toBeA('array'); + + foreach ($result as $char) { + \expect($char)->toBeAnInstanceOf(__('Stringy')); + } + + for ($i = 0; $i < count($expected); $i++) { + \expect((string) $result[$i])->toBe($expected[$i]); + } + } + }); + + it('test upperCaseFirst', function () { + $data = [ + ['Test', 'Test'], + ['Test', 'test'], + ['1a', '1a'], + ['Σ test', 'σ test', 'UTF-8'], + [' σ test', ' σ test', 'UTF-8'], + ]; + foreach ($data as $value) { + @list($expected, $str, $encoding) = $value; + $result = __('Stringy')::create($str, $encoding)->upperCaseFirst(); + \expect($result)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $result)->toBe($expected); + } + }); + + it('test lowerCaseFirst', function () { + $data = [ + ['test', 'Test'], + ['test', 'test'], + ['1a', '1a'], + ['σ test', 'Σ test', 'UTF-8'], + [' Σ test', ' Σ test', 'UTF-8'], + ]; + + foreach ($data as $value) { + @list($expected, $str, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + \expect((string) $stringy)->toBe($str); + $result = $stringy->lowerCaseFirst(); + \expect($result)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + }); + + it('test testCamelize', function () { + $data = [ + ['camelCase', 'CamelCase'], + ['camelCase', 'Camel-Case'], + ['camelCase', 'camel case'], + ['camelCase', 'camel -case'], + ['camelCase', 'camel - case'], + ['camelCase', 'camel_case'], + ['camelCTest', 'camel c test'], + ['stringWith1Number', 'string_with1number'], + ['stringWith22Numbers', 'string-with-2-2 numbers'], + ['dataRate', 'data_rate'], + ['backgroundColor', 'background-color'], + ['yesWeCan', 'yes_we_can'], + ['mozSomething', '-moz-something'], + ['carSpeed', '_car_speed_'], + ['serveHTTP', 'ServeHTTP'], + ['1Camel2Case', '1camel2case'], + ['camelΣase', 'camel σase', 'UTF-8'], + ['στανιλCase', 'Στανιλ case', 'UTF-8'], + ['σamelCase', 'σamel Case', 'UTF-8'], + ]; + foreach ($data as $value) { + @list($expected, $str, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->camelize(); + \expect($result)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + }); + + it('test regexReplce', function () { + $data = [ + ['', '', '', ''], + ['bar', 'foo', 'f[o]+', 'bar'], + ['o bar', 'foo bar', 'f(o)o', '\1'], + ['bar', 'foo bar', 'f[O]+\s', '', 'i'], + ['foo', 'bar', '[[:alpha:]]{3}', 'foo'], + ['', '', '', '', 'msr', 'UTF-8'], + ['bàř', 'fòô ', 'f[òô]+\s', 'bàř', 'msr', 'UTF-8'], + ['fòô', 'fò', '(ò)', '\\1ô', 'msr', 'UTF-8'], + ['fòô', 'bàř', '[[:alpha:]]{3}', 'fòô', 'msr', 'UTF-8'], + ]; + + foreach ($data as $value) { + @list($expected, $str, $pattern, $replacement, $options, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->regexReplace($pattern, $replacement, $options); + \expect($result)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + }); + + it('test trim', function () { + $data = [ + ['foo bar', ' foo bar '], + ['foo bar', ' foo bar'], + ['foo bar', 'foo bar '], + ['foo bar', "\n\t foo bar \n\t"], + ['fòô bàř', ' fòô bàř '], + ['fòô bàř', ' fòô bàř'], + ['fòô bàř', 'fòô bàř '], + [' foo bar ', "\n\t foo bar \n\t", "\n\t"], + ['fòô bàř', "\n\t fòô bàř \n\t", null, 'UTF-8'], + ['fòô', ' fòô ', null, 'UTF-8'], // narrow no-break space (U+202F) + ['fòô', '  fòô  ', null, 'UTF-8'], // medium mathematical space (U+205F) + ['fòô', '           fòô', null, 'UTF-8'], // spaces U+2000 to U+200A + ]; + + foreach ($data as $value) { + @list($expected, $str, $chars, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->trim($chars); + + \expect($result)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + }); + + it('test trimLeft', function () { + $data = [ + ['foo bar ', ' foo bar '], + ['foo bar', ' foo bar'], + ['foo bar ', 'foo bar '], + ["foo bar \n\t", "\n\t foo bar \n\t"], + ['fòô bàř ', ' fòô bàř '], + ['fòô bàř', ' fòô bàř'], + ['fòô bàř ', 'fòô bàř '], + ['foo bar', '--foo bar', '-'], + ['fòô bàř', 'òòfòô bàř', 'ò', 'UTF-8'], + ["fòô bàř \n\t", "\n\t fòô bàř \n\t", null, 'UTF-8'], + ['fòô ', ' fòô ', null, 'UTF-8'], // narrow no-break space (U+202F) + ['fòô  ', '  fòô  ', null, 'UTF-8'], // medium mathematical space (U+205F) + ['fòô', '           fòô', null, 'UTF-8'], // spaces U+2000 to U+200A + ]; + + foreach ($data as $value) { + @list($expected, $str, $chars, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->trimLeft($chars); + + \expect($result)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + }); + + it('test trimRight', function () { + $data = [ + [' foo bar', ' foo bar '], + ['foo bar', 'foo bar '], + [' foo bar', ' foo bar'], + ["\n\t foo bar", "\n\t foo bar \n\t"], + [' fòô bàř', ' fòô bàř '], + ['fòô bàř', 'fòô bàř '], + [' fòô bàř', ' fòô bàř'], + ['foo bar', 'foo bar--', '-'], + ['fòô bàř', 'fòô bàřòò', 'ò', 'UTF-8'], + ["\n\t fòô bàř", "\n\t fòô bàř \n\t", null, 'UTF-8'], + [' fòô', ' fòô ', null, 'UTF-8'], // narrow no-break space (U+202F) + ['  fòô', '  fòô  ', null, 'UTF-8'], // medium mathematical space (U+205F) + ['fòô', 'fòô           ', null, 'UTF-8'], // spaces U+2000 to U+200A + ]; + + foreach ($data as $value) { + @list($expected, $str, $chars, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->trimRight($chars); + + \expect($result)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + }); + + it('test at', function () { + $data = [ + ['f', 'foo bar', 0], + ['o', 'foo bar', 1], + ['r', 'foo bar', 6], + ['', 'foo bar', 7], + ['f', 'fòô bàř', 0, 'UTF-8'], + ['ò', 'fòô bàř', 1, 'UTF-8'], + ['ř', 'fòô bàř', 6, 'UTF-8'], + ['', 'fòô bàř', 7, 'UTF-8'], + ]; + + foreach ($data as $value) { + @list($expected, $str, $index, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->at($index); + \expect($result)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + }); + + it('test between', function () { + $data = [ + ['', 'foo', '{', '}'], + ['', '{foo', '{', '}'], + ['foo', '{foo}', '{', '}'], + ['{foo', '{{foo}', '{', '}'], + ['', '{}foo}', '{', '}'], + ['foo', '}{foo}', '{', '}'], + ['foo', 'A description of {foo} goes here', '{', '}'], + ['bar', '{foo} and {bar}', '{', '}', 1], + ['', 'fòô', '{', '}', 0, 'UTF-8'], + ['', '{fòô', '{', '}', 0, 'UTF-8'], + ['fòô', '{fòô}', '{', '}', 0, 'UTF-8'], + ['{fòô', '{{fòô}', '{', '}', 0, 'UTF-8'], + ['', '{}fòô}', '{', '}', 0, 'UTF-8'], + ['fòô', '}{fòô}', '{', '}', 0, 'UTF-8'], + ['fòô', 'A description of {fòô} goes here', '{', '}', 0, 'UTF-8'], + ['bàř', '{fòô} and {bàř}', '{', '}', 1, 'UTF-8'], + ]; + foreach ($data as $value) { + @list($expected, $str, $start, $end, $offset, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->between($start, $end, $offset); + \expect($result)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + }); + + \context('contains', function () { + $data = [ + [true, 'Str contains foo bar', 'foo bar'], + [true, '12398!@(*%!@# @!%#*&^%', ' @!%#*&^%'], + [true, 'Ο συγγραφέας είπε', 'συγγραφέας', 'UTF-8'], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', 'å´¥©', true, 'UTF-8'], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', 'å˚ ∆', true, 'UTF-8'], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', 'øœ¬', true, 'UTF-8'], + [false, 'Str contains foo bar', 'Foo bar'], + [false, 'Str contains foo bar', 'foobar'], + [false, 'Str contains foo bar', 'foo bar '], + [false, 'Ο συγγραφέας είπε', ' συγγραφέας ', true, 'UTF-8'], + [false, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ' ßå˚', true, 'UTF-8'], + [true, 'Str contains foo bar', 'Foo bar', false], + [true, '12398!@(*%!@# @!%#*&^%', ' @!%#*&^%', false], + [true, 'Ο συγγραφέας είπε', 'ΣΥΓΓΡΑΦΈΑΣ', false, 'UTF-8'], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', 'Å´¥©', false, 'UTF-8'], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', 'Å˚ ∆', false, 'UTF-8'], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', 'ØŒ¬', false, 'UTF-8'], + [false, 'Str contains foo bar', 'foobar', false], + [false, 'Str contains foo bar', 'foo bar ', false], + [false, 'Ο συγγραφέας είπε', ' συγγραφέας ', false, 'UTF-8'], + [false, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ' ßÅ˚', false, 'UTF-8'], + ]; + + $dataContainsAll = array_merge( + array_map(function ($array) { + $array[2] = [$array[2]]; + + return $array; + }, $data), + [ + // One needle + [false, 'Str contains foo bar', []], + // Multiple needles + [true, 'Str contains foo bar', ['foo', 'bar']], + [true, '12398!@(*%!@# @!%#*&^%', [' @!%#*', '&^%']], + [true, 'Ο συγγραφέας είπε', ['συγγρ', 'αφέας'], 'UTF-8'], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['å´¥', '©'], true, 'UTF-8'], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['å˚ ', '∆'], true, 'UTF-8'], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['øœ', '¬'], true, 'UTF-8'], + [false, 'Str contains foo bar', ['Foo', 'bar']], + [false, 'Str contains foo bar', ['foobar', 'bar']], + [false, 'Str contains foo bar', ['foo bar ', 'bar']], + [false, 'Ο συγγραφέας είπε', [' συγγραφέας ', ' συγγραφ '], true, 'UTF-8'], + [false, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', [' ßå˚', ' ß '], true, 'UTF-8'], + [true, 'Str contains foo bar', ['Foo bar', 'bar'], false], + [true, '12398!@(*%!@# @!%#*&^%', [' @!%#*&^%', '*&^%'], false], + [true, 'Ο συγγραφέας είπε', ['ΣΥΓΓΡΑΦΈΑΣ', 'ΑΦΈΑ'], false, 'UTF-8'], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['Å´¥©', '¥©'], false, 'UTF-8'], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['Å˚ ∆', ' ∆'], false, 'UTF-8'], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['ØŒ¬', 'Œ'], false, 'UTF-8'], + [false, 'Str contains foo bar', ['foobar', 'none'], false], + [false, 'Str contains foo bar', ['foo bar ', ' ba'], false], + [false, 'Ο συγγραφέας είπε', [' συγγραφέας ', ' ραφέ '], false, 'UTF-8'], + [false, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', [' ßÅ˚', ' Å˚ '], false, 'UTF-8'], + ]); + + $dataContainsAny = array_merge( + array_map(function ($array) { + $array[2] = [$array[2]]; + + return $array; + }, $data), + [ + // No needles + [false, 'Str contains foo bar', []], + // Multiple needles + [true, 'Str contains foo bar', ['foo', 'bar']], + [true, '12398!@(*%!@# @!%#*&^%', [' @!%#*', '&^%']], + [true, 'Ο συγγραφέας είπε', ['συγγρ', 'αφέας'], 'UTF-8'], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['å´¥', '©'], true, 'UTF-8'], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['å˚ ', '∆'], true, 'UTF-8'], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['øœ', '¬'], true, 'UTF-8'], + [false, 'Str contains foo bar', ['Foo', 'Bar']], + [false, 'Str contains foo bar', ['foobar', 'bar ']], + [false, 'Str contains foo bar', ['foo bar ', ' foo']], + [false, 'Ο συγγραφέας είπε', [' συγγραφέας ', ' συγγραφ '], true, 'UTF-8'], + [false, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', [' ßå˚', ' ß '], true, 'UTF-8'], + [true, 'Str contains foo bar', ['Foo bar', 'bar'], false], + [true, '12398!@(*%!@# @!%#*&^%', [' @!%#*&^%', '*&^%'], false], + [true, 'Ο συγγραφέας είπε', ['ΣΥΓΓΡΑΦΈΑΣ', 'ΑΦΈΑ'], false, 'UTF-8'], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['Å´¥©', '¥©'], false, 'UTF-8'], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['Å˚ ∆', ' ∆'], false, 'UTF-8'], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['ØŒ¬', 'Œ'], false, 'UTF-8'], + [false, 'Str contains foo bar', ['foobar', 'none'], false], + [false, 'Str contains foo bar', ['foo bar ', ' ba '], false], + [false, 'Ο συγγραφέας είπε', [' συγγραφέας ', ' ραφέ '], false, 'UTF-8'], + [false, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', [' ßÅ˚', ' Å˚ '], false, 'UTF-8'], + ]); + + it('test contains', function () use ($data) { + foreach ($data as $value) { + @list($expected, $haystack, $needle, $caseSensitive, $encoding) = $value; + $stringy = __('Stringy')::create($haystack, $encoding); + $result = $stringy->contains($needle, $caseSensitive === true || $caseSensitive === null); // 默认值是 NULl, 这里需要特殊处理 + \expect($result)->toBeA('bool'); + \expect((string) $stringy)->toBe($haystack); + \expect((bool) $result)->toBe($expected); + } + }); + + it('test containsAll', function () use ($dataContainsAll) { + foreach ($dataContainsAll as $value) { + @list($expected, $haystack, $needle, $caseSensitive, $encoding) = $value; + + $stringy = __('Stringy')::create($haystack, $encoding); + $result = $stringy->containsAll($needle, $caseSensitive === true || $caseSensitive === null); // 默认值是 NULl, 这里需要特殊处理 + \expect($result)->toBeA('bool'); + \expect((string) $stringy)->toBe($haystack); + \expect((bool) $result)->toBe($expected); + } + }); + + it('test containsAny', function () use ($dataContainsAny) { + foreach ($dataContainsAny as $value) { + @list($expected, $haystack, $needles, $caseSensitive, $encoding) = $value; + $stringy = __('Stringy')::create($haystack, $encoding); + + $result = $stringy->containsAny($needles, $caseSensitive === true || $caseSensitive === null); + \expect($result)->toBeA('bool'); + \expect((string) $stringy)->toBe($haystack); + \expect((bool) $result)->toBe($expected); + } + }); + }); + + it('test split', function () { + $data = [ + [['foo,bar,baz'], 'foo,bar,baz', ''], + [['foo,bar,baz'], 'foo,bar,baz', '-'], + [['foo', 'bar', 'baz'], 'foo,bar,baz', ','], + [['foo', 'bar', 'baz'], 'foo,bar,baz', ',', null], + [['foo', 'bar', 'baz'], 'foo,bar,baz', ',', -1], + [[], 'foo,bar,baz', ',', 0], + [['foo'], 'foo,bar,baz', ',', 1], + [['foo', 'bar'], 'foo,bar,baz', ',', 2], + [['foo', 'bar', 'baz'], 'foo,bar,baz', ',', 3], + [['foo', 'bar', 'baz'], 'foo,bar,baz', ',', 10], + [['fòô,bàř,baz'], 'fòô,bàř,baz', '-', null, 'UTF-8'], + [['fòô', 'bàř', 'baz'], 'fòô,bàř,baz', ',', null, 'UTF-8'], + [['fòô', 'bàř', 'baz'], 'fòô,bàř,baz', ',', null, 'UTF-8'], + [['fòô', 'bàř', 'baz'], 'fòô,bàř,baz', ',', -1, 'UTF-8'], + [[], 'fòô,bàř,baz', ',', 0, 'UTF-8'], + [['fòô'], 'fòô,bàř,baz', ',', 1, 'UTF-8'], + [['fòô', 'bàř'], 'fòô,bàř,baz', ',', 2, 'UTF-8'], + [['fòô', 'bàř', 'baz'], 'fòô,bàř,baz', ',', 3, 'UTF-8'], + [['fòô', 'bàř', 'baz'], 'fòô,bàř,baz', ',', 10, 'UTF-8'], + ]; + + foreach ($data as $value) { + @list($expected, $str, $pattern, $limit, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->split($pattern, $limit); + \expect($result)->toBeA('array'); + foreach ($result as $string) { + \expect($string)->toBeAnInstanceOf(__('Stringy')); + } + for ($i = 0; $i < count($expected); $i++) { + \expect((string) $result[$i])->toBe($expected[$i]); + } + } + }); + + it('test CollapseWhitespace', function () { + $data = [ + ['foo bar', ' foo bar '], + ['test string', 'test string'], + ['Ο συγγραφέας', ' Ο συγγραφέας '], + ['123', ' 123 '], + ['', ' ', 'UTF-8'], // no-break space (U+00A0) + ['', '           ', 'UTF-8'], // spaces U+2000 to U+200A + ['', ' ', 'UTF-8'], // narrow no-break space (U+202F) + ['', ' ', 'UTF-8'], // medium mathematical space (U+205F) + ['', ' ', 'UTF-8'], // ideographic space (U+3000) + ['1 2 3', ' 1  2  3  ', 'UTF-8'], + ['', ' '], + ['', ''], + ]; + + foreach ($data as $value) { + @list($expected, $str, $encoding) = $value; + { + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->collapseWhitespace(); + \expect($result)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + } + }); + + $data = [ + [6, 'foo & bar', 'bar'], + [6, 'foo & bar', 'bar', 0], + [false, 'foo & bar', 'baz'], + [false, 'foo & bar', 'baz', 0], + [0, 'foo & bar & foo', 'foo', 0], + [12, 'foo & bar & foo', 'foo', 5], + [6, 'fòô & bàř', 'bàř', 0, 'UTF-8'], + [false, 'fòô & bàř', 'baz', 0, 'UTF-8'], + [0, 'fòô & bàř & fòô', 'fòô', 0, 'UTF-8'], + [12, 'fòô & bàř & fòô', 'fòô', 5, 'UTF-8'], + ]; + + it('test indexOf', function () use ($data) { + foreach ($data as $value) { + @list($expected, $str, $subStr, $offset, $encoding) = $value; + $result = __('Stringy')::create($str, $encoding)->indexOf($subStr, $offset); + \expect($result)->toBe($expected); + } + }); + + it('test countSubStr', function () { + $data = [ + [0, '', 'foo'], + [0, 'foo', 'bar'], + [1, 'foo bar', 'foo'], + [2, 'foo bar', 'o'], + [0, '', 'fòô', true, 'UTF-8'], + [0, 'fòô', 'bàř', true, 'UTF-8'], + [1, 'fòô bàř', 'fòô', true, 'UTF-8'], + [2, 'fôòô bàř', 'ô', true, 'UTF-8'], + [0, 'fÔÒÔ bàř', 'ô', true, 'UTF-8'], + [0, 'foo', 'BAR', false], + [1, 'foo bar', 'FOo', false], + [2, 'foo bar', 'O', false], + [1, 'fòô bàř', 'fÒÔ', false, 'UTF-8'], + [2, 'fôòô bàř', 'Ô', false, 'UTF-8'], + [2, 'συγγραφέας', 'Σ', false, 'UTF-8'], + ]; + + foreach ($data as $value) { + @list($expected, $str, $substring, $caseSensitive, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->countSubstr($substring, $caseSensitive === true || $caseSensitive === null); + \expect($result)->toBeA('int'); + \expect($result)->toBe($expected); + \expect((string) $stringy)->toBe($str); + } + }); + + it('test delimit', function () { + $data = [ + ['test*case', 'testCase', '*'], + ['test&case', 'Test-Case', '&'], + ['test#case', 'test case', '#'], + ['test**case', 'test -case', '**'], + ['~!~test~!~case', '-test - case', '~!~'], + ['test*case', 'test_case', '*'], + ['test%c%test', ' test c test', '%'], + ['test+u+case', 'TestUCase', '+'], + ['test=c=c=test', 'TestCCTest', '='], + ['string#>with1number', 'string_with1number', '#>'], + ['1test2case', '1test2case', '*'], + ['test ύα σase', 'test Σase', ' ύα ', 'UTF-8'], + ['στανιλαcase', 'Στανιλ case', 'α', 'UTF-8'], + ['σashΘcase', 'Σash Case', 'Θ', 'UTF-8'], + ]; + + foreach ($data as $value) { + @list($expected, $str, $delimiter, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->delimit($delimiter); + + \expect($result)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + }); + + it('test dasherize', function () { + $data = [ + ['test-case', 'testCase'], + ['test-case', 'Test-Case'], + ['test-case', 'test case'], + ['-test-case', '-test -case'], + ['test-case', 'test - case'], + ['test-case', 'test_case'], + ['test-c-test', 'test c test'], + ['test-d-case', 'TestDCase'], + ['test-c-c-test', 'TestCCTest'], + ['string-with1number', 'string_with1number'], + ['string-with-2-2-numbers', 'String-with_2_2 numbers'], + ['1test2case', '1test2case'], + ['data-rate', 'dataRate'], + ['car-speed', 'CarSpeed'], + ['yes-we-can', 'yesWeCan'], + ['background-color', 'backgroundColor'], + ['dash-σase', 'dash Σase', 'UTF-8'], + ['στανιλ-case', 'Στανιλ case', 'UTF-8'], + ['σash-case', 'Σash Case', 'UTF-8'], + ]; + + foreach ($data as $value) { + @list($expected, $str, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->dasherize(); + \expect($result)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + }); + + it('test endsWith', function () { + $data = [ + [true, 'foo bars', 'o bars'], + [true, 'FOO bars', 'o bars', false], + [true, 'FOO bars', 'o BARs', false], + [true, 'FÒÔ bàřs', 'ô bàřs', false, 'UTF-8'], + [true, 'fòô bàřs', 'ô BÀŘs', false, 'UTF-8'], + [false, 'foo bar', 'foo'], + [false, 'foo bar', 'foo bars'], + [false, 'FOO bar', 'foo bars'], + [false, 'FOO bars', 'foo BARS'], + [false, 'FÒÔ bàřs', 'fòô bàřs', true, 'UTF-8'], + [false, 'fòô bàřs', 'fòô BÀŘS', true, 'UTF-8'], + ]; + foreach ($data as $value) { + @list($expected, $str, $substring, $caseSensitive, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->endsWith($substring, $caseSensitive === true || $caseSensitive === null); + + \expect($result)->toBeA('boolean'); + \expect((string) $stringy)->toBe($str); + \expect($result)->toBe($expected); + } + }); + + it('test endWithAny', function () { + $data = [ + [true, 'foo bars', ['foo', 'o bars']], + [true, 'FOO bars', ['foo', 'o bars'], false], + [true, 'FOO bars', ['foo', 'o BARs'], false], + [true, 'FÒÔ bàřs', ['foo', 'ô bàřs'], false, 'UTF-8'], + [true, 'fòô bàřs', ['foo', 'ô BÀŘs'], false, 'UTF-8'], + [false, 'foo bar', ['foo']], + [false, 'foo bar', ['foo', 'foo bars']], + [false, 'FOO bar', ['foo', 'foo bars']], + [false, 'FOO bars', ['foo', 'foo BARS']], + [false, 'FÒÔ bàřs', ['fòô', 'fòô bàřs'], true, 'UTF-8'], + [false, 'fòô bàřs', ['fòô', 'fòô BÀŘS'], true, 'UTF-8'], + [false, 'fòô bàřs', [], true, 'UTF-8'], + ]; + + foreach ($data as $value) { + @list($expected, $str, $substrings, $caseSensitive, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->endsWithAny($substrings, $caseSensitive === true || $caseSensitive === null); + + \expect($result)->toBeA('boolean'); + \expect((string) $stringy)->toBe($str); + \expect($result)->toBe($expected); + } + }); + + it('test ensureLeft', function () { + $data = [ + ['foobar', 'foobar', 'f'], + ['foobar', 'foobar', 'foo'], + ['foo/foobar', 'foobar', 'foo/'], + ['http://foobar', 'foobar', 'http://'], + ['http://foobar', 'http://foobar', 'http://'], + ['fòôbàř', 'fòôbàř', 'f', 'UTF-8'], + ['fòôbàř', 'fòôbàř', 'fòô', 'UTF-8'], + ['fòô/fòôbàř', 'fòôbàř', 'fòô/', 'UTF-8'], + ['http://fòôbàř', 'fòôbàř', 'http://', 'UTF-8'], + ['http://fòôbàř', 'http://fòôbàř', 'http://', 'UTF-8'], + ]; + + foreach ($data as $value) { + @list($expected, $str, $substring, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->ensureLeft($substring); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + }); + + it('test startWith', function () { + $data = [ + [true, 'foo bars', 'foo bar'], + [true, 'FOO bars', 'foo bar', false], + [true, 'FOO bars', 'foo BAR', false], + [true, 'FÒÔ bàřs', 'fòô bàř', false, 'UTF-8'], + [true, 'fòô bàřs', 'fòô BÀŘ', false, 'UTF-8'], + [false, 'foo bar', 'bar'], + [false, 'foo bar', 'foo bars'], + [false, 'FOO bar', 'foo bars'], + [false, 'FOO bars', 'foo BAR'], + [false, 'FÒÔ bàřs', 'fòô bàř', true, 'UTF-8'], + [false, 'fòô bàřs', 'fòô BÀŘ', true, 'UTF-8'], + ]; + foreach ($data as $value) { + @list($expected, $str, $substring, $caseSensitive, $encoding) = $value; + $stringy = _('Stringy')::create($str, $encoding); + $result = $stringy->startsWith($substring, $caseSensitive === true || $caseSensitive === null); + + \expect($result)->toBeA('boolean'); + \expect((string) $stringy)->toBe($str); + \expect($result)->toBe($expected); + } + }); + + it('test ensureRight', function () { + $data = [ + ['foobar', 'foobar', 'r'], + ['foobar', 'foobar', 'bar'], + ['foobar/bar', 'foobar', '/bar'], + ['foobar.com/', 'foobar', '.com/'], + ['foobar.com/', 'foobar.com/', '.com/'], + ['fòôbàř', 'fòôbàř', 'ř', 'UTF-8'], + ['fòôbàř', 'fòôbàř', 'bàř', 'UTF-8'], + ['fòôbàř/bàř', 'fòôbàř', '/bàř', 'UTF-8'], + ['fòôbàř.com/', 'fòôbàř', '.com/', 'UTF-8'], + ['fòôbàř.com/', 'fòôbàř.com/', '.com/', 'UTF-8'], + ]; + + foreach ($data as $value) { + @list($expected, $str, $substring, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->ensureRight($substring); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + }); + + it('test first', function () { + $data = [ + ['', 'foo bar', -5], + ['', 'foo bar', 0], + ['f', 'foo bar', 1], + ['foo', 'foo bar', 3], + ['foo bar', 'foo bar', 7], + ['foo bar', 'foo bar', 8], + ['', 'fòô bàř', -5, 'UTF-8'], + ['', 'fòô bàř', 0, 'UTF-8'], + ['f', 'fòô bàř', 1, 'UTF-8'], + ['fòô', 'fòô bàř', 3, 'UTF-8'], + ['fòô bàř', 'fòô bàř', 7, 'UTF-8'], + ['fòô bàř', 'fòô bàř', 8, 'UTF-8'], + ]; + + foreach ($data as $value) { + @list($expected, $str, $n, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->first($n); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + }); + + it('test isAlpha', function () { + $data = [ + [true, ''], + [true, 'foobar'], + [false, 'foo bar'], + [false, 'foobar2'], + [true, 'fòôbàř', 'UTF-8'], + [false, 'fòô bàř', 'UTF-8'], + [false, 'fòôbàř2', 'UTF-8'], + [true, 'ҠѨњфгШ', 'UTF-8'], + [false, 'ҠѨњ¨ˆфгШ', 'UTF-8'], + [true, '丹尼爾', 'UTF-8'], + ]; + foreach ($data as $value) { + @list($expected, $str, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->isAlpha(); + + \expect($result)->toBeA('boolean'); + \expect((string) $stringy)->toBe($str); + \expect($result)->toBe($expected); + } + }); + + it('test isBlank', function () { + $data = [ + [true, ''], + [true, ' '], + [true, "\n\t "], + [true, "\n\t \v\f"], + [false, "\n\t a \v\f"], + [false, "\n\t ' \v\f"], + [false, "\n\t 2 \v\f"], + [true, '', 'UTF-8'], + [true, ' ', 'UTF-8'], // no-break space (U+00A0) + [true, '           ', 'UTF-8'], // spaces U+2000 to U+200A + [true, ' ', 'UTF-8'], // narrow no-break space (U+202F) + [true, ' ', 'UTF-8'], // medium mathematical space (U+205F) + [true, ' ', 'UTF-8'], // ideographic space (U+3000) + [false, ' z', 'UTF-8'], + [false, ' 1', 'UTF-8'], + ]; + foreach ($data as $value) + { + @list($expected, $str, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->isBlank(); + + \expect($result)->toBeA('boolean'); + \expect((string) $stringy)->toBe($str); + \expect($result)->toBe($expected); + } + }); + + it('test hasLowerCase', function () { + $data = [ + [false, ''], + [true, 'foobar'], + [false, 'FOO BAR'], + [true, 'fOO BAR'], + [true, 'foO BAR'], + [true, 'FOO BAr'], + [true, 'Foobar'], + [false, 'FÒÔBÀŘ', 'UTF-8'], + [true, 'fòôbàř', 'UTF-8'], + [true, 'fòôbàř2', 'UTF-8'], + [true, 'Fòô bàř', 'UTF-8'], + [true, 'fòôbÀŘ', 'UTF-8'], + ]; + + foreach ($data as $value) { + @list($expected, $str, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->hasLowerCase(); + + \expect($result)->toBeA('boolean'); + \expect((string) $stringy)->toBe($str); + \expect($result)->toBe($expected); + } + }); + + + it('test hasUpperCase', function () { + $data = [ + [false, ''], + [true, 'FOOBAR'], + [false, 'foo bar'], + [true, 'Foo bar'], + [true, 'FOo bar'], + [true, 'foo baR'], + [true, 'fOOBAR'], + [false, 'fòôbàř', 'UTF-8'], + [true, 'FÒÔBÀŘ', 'UTF-8'], + [true, 'FÒÔBÀŘ2', 'UTF-8'], + [true, 'fÒÔ BÀŘ', 'UTF-8'], + [true, 'FÒÔBàř', 'UTF-8'], + ]; + + foreach ($data as $value) { + @list($expected, $str, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->hasUpperCase(); + + \expect($result)->toBeA('boolean'); + \expect((string) $stringy)->toBe($str); + \expect($result)->toBe($expected); + } + }); + + it('test htmlDecode', function () { + $data = [ + ['&', '&'], + ['"', '"'], + ["'", ''', ENT_QUOTES], + ['<', '<'], + ['>', '>'], + ]; + foreach($data as $value){ + @list($expected, $str, $flags, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->htmlDecode($flags ?? ENT_COMPAT); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + }); + + it('test htmlEncode', function () { + $data = [ + ['&', '&'], + ['"', '"'], + [''', "'", ENT_QUOTES], + ['<', '<'], + ['>', '>'], + ]; + foreach($data as $value){ + @list($expected, $str, $flags, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->htmlEncode($flags ?? ENT_COMPAT); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + }); + + it('test substr', function () { + $data = [ + ['foo bar', 'foo bar', 0], + ['bar', 'foo bar', 4], + ['bar', 'foo bar', 4, null], + ['o b', 'foo bar', 2, 3], + ['', 'foo bar', 4, 0], + ['fòô bàř', 'fòô bàř', 0, null, 'UTF-8'], + ['bàř', 'fòô bàř', 4, null, 'UTF-8'], + ['ô b', 'fòô bàř', 2, 3, 'UTF-8'], + ['', 'fòô bàř', 4, 0, 'UTF-8'], + ['r', 'foo bar', -1], + ]; + + foreach ($data as $value) { + @list($expected, $str, $start, $length, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->substr($start, $length); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + + }); + + it('test last', function () { + $data = [ + ['', 'foo bar', -5], + ['', 'foo bar', 0], + ['r', 'foo bar', 1], + ['bar', 'foo bar', 3], + ['foo bar', 'foo bar', 7], + ['foo bar', 'foo bar', 8], + ['', 'fòô bàř', -5, 'UTF-8'], + ['', 'fòô bàř', 0, 'UTF-8'], + ['ř', 'fòô bàř', 1, 'UTF-8'], + ['bàř', 'fòô bàř', 3, 'UTF-8'], + ['fòô bàř', 'fòô bàř', 7, 'UTF-8'], + ['fòô bàř', 'fòô bàř', 8, 'UTF-8'], + ]; + + foreach ($data as $value) { + @list($expected, $str, $n, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->last($n); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + }); + + it('test removeLeft', function () { + $data = [ + ['foo bar', 'foo bar', ''], + ['oo bar', 'foo bar', 'f'], + ['bar', 'foo bar', 'foo '], + ['foo bar', 'foo bar', 'oo'], + ['foo bar', 'foo bar', 'oo bar'], + ['oo bar', 'foo bar', __('Stringy')::create('foo bar')->first(1), 'UTF-8'], + ['oo bar', 'foo bar', __('Stringy')::create('foo bar')->at(0), 'UTF-8'], + ['fòô bàř', 'fòô bàř', '', 'UTF-8'], + ['òô bàř', 'fòô bàř', 'f', 'UTF-8'], + ['bàř', 'fòô bàř', 'fòô ', 'UTF-8'], + ['fòô bàř', 'fòô bàř', 'òô', 'UTF-8'], + ['fòô bàř', 'fòô bàř', 'òô bàř', 'UTF-8'] + ]; + foreach($data as $value) + { + @list($expected, $str, $substring, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->removeLeft((string) $substring); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + }); + + it('test removeRight', function () { + $data = [ + ['foo bar', 'foo bar', ''], + ['foo ba', 'foo bar', 'r'], + ['foo', 'foo bar', ' bar'], + ['foo bar', 'foo bar', 'ba'], + ['foo bar', 'foo bar', 'foo ba'], + ['foo ba', 'foo bar', __('Stringy')::create('foo bar')->last(1), 'UTF-8'], + ['foo ba', 'foo bar', __('Stringy')::create('foo bar')->at(6), 'UTF-8'], + ['fòô bàř', 'fòô bàř', '', 'UTF-8'], + ['fòô bà', 'fòô bàř', 'ř', 'UTF-8'], + ['fòô', 'fòô bàř', ' bàř', 'UTF-8'], + ['fòô bàř', 'fòô bàř', 'bà', 'UTF-8'], + ['fòô bàř', 'fòô bàř', 'fòô bà', 'UTF-8'] + ]; + foreach($data as $value) + { + @list($expected, $str, $substring, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->removeRight((string) $substring); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + }); + + it('test humanize', function () { + $data = [ + ['Author', 'author_id'], + ['Test user', ' _test_user_'], + ['Συγγραφέας', ' συγγραφέας_id ', 'UTF-8'] + ]; + + foreach($data as $value) { + @list($expected, $str, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->humanize(); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + } + }); + + it('test insert', function () { + $data = [ + ['foo bar', 'oo bar', 'f', 0], + ['foo bar', 'f bar', 'oo', 1], + ['f bar', 'f bar', 'oo', 20], + ['foo bar', 'foo ba', 'r', 6], + ['fòôbàř', 'fòôbř', 'à', 4, 'UTF-8'], + ['fòô bàř', 'òô bàř', 'f', 0, 'UTF-8'], + ['fòô bàř', 'f bàř', 'òô', 1, 'UTF-8'], + ['fòô bàř', 'fòô bà', 'ř', 6, 'UTF-8'] + ]; + foreach($data as $value) { + @list($expected, $str, $substring, $index, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->insert($substring, $index); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + + } + }); + + it('test isAlphanumeric', function () { + $data = [ + [true, ''], + [true, 'foobar1'], + [false, 'foo bar'], + [false, 'foobar2"'], + [false, "\nfoobar\n"], + [true, 'fòôbàř1', 'UTF-8'], + [false, 'fòô bàř', 'UTF-8'], + [false, 'fòôbàř2"', 'UTF-8'], + [true, 'ҠѨњфгШ', 'UTF-8'], + [false, 'ҠѨњ¨ˆфгШ', 'UTF-8'], + [true, '丹尼爾111', 'UTF-8'], + [true, 'دانيال1', 'UTF-8'], + [false, 'دانيال1 ', 'UTF-8'] + ]; + foreach($data as $value) { + @list($expected, $str, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->isAlphanumeric(); + + \expect($result)->toBeA('boolean'); + \expect((string) $stringy)->toBe($str); + \expect((bool) $result)->toBe($expected); + } + }); + + it('test isHexadecimal', function () { + $data = [ + [true, ''], + [true, 'abcdef'], + [true, 'ABCDEF'], + [true, '0123456789'], + [true, '0123456789AbCdEf'], + [false, '0123456789x'], + [false, 'ABCDEFx'], + [true, 'abcdef', 'UTF-8'], + [true, 'ABCDEF', 'UTF-8'], + [true, '0123456789', 'UTF-8'], + [true, '0123456789AbCdEf', 'UTF-8'], + [false, '0123456789x', 'UTF-8'], + [false, 'ABCDEFx', 'UTF-8'], + ]; + foreach($data as $value) { + @list($expected, $str, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->isHexadecimal(); + + \expect($result)->toBeA('boolean'); + \expect((string) $stringy)->toBe($str); + \expect((bool) $result)->toBe($expected); + } + }); + + + it('test isLowerCase', function () { + $data = [ + [true, ''], + [true, 'foobar'], + [false, 'foo bar'], + [false, 'Foobar'], + [true, 'fòôbàř', 'UTF-8'], + [false, 'fòôbàř2', 'UTF-8'], + [false, 'fòô bàř', 'UTF-8'], + [false, 'fòôbÀŘ', 'UTF-8'], + ]; + foreach($data as $value) { + @list($expected, $str, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->isLowerCase(); + + \expect($result)->toBeA('boolean'); + \expect((string) $stringy)->toBe($str); + \expect((bool) $result)->toBe($expected); + } + }); + + it('test isUpperCase', function () { + $data = [ + [true, ''], + [true, 'FOOBAR'], + [false, 'FOO BAR'], + [false, 'fOOBAR'], + [true, 'FÒÔBÀŘ', 'UTF-8'], + [false, 'FÒÔBÀŘ2', 'UTF-8'], + [false, 'FÒÔ BÀŘ', 'UTF-8'], + [false, 'FÒÔBàř', 'UTF-8'], + ]; + foreach($data as $value) { + @list($expected, $str, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->isUpperCase(); + + \expect($result)->toBeA('boolean'); + \expect((string) $stringy)->toBe($str); + \expect((bool) $result)->toBe($expected); + } + }); + + it('test length', function () { + $data = [ + [11, ' foo bar '], + [1, 'f'], + [0, ''], + [7, 'fòô bàř', 'UTF-8'] + ]; + + foreach($data as $value) { + @list($expected, $str, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->length(); + + \expect($result)->toBeA('int'); + \expect((string) $stringy)->toBe($str); + \expect((int) $result)->toBe($expected); + } + }); + + it('test isJson', function () { + $data = [ + [false, ''], + [false, ' '], + [true, 'null'], + [true, 'true'], + [true, 'false'], + [true, '[]'], + [true, '{}'], + [true, '123'], + [true, '{"foo": "bar"}'], + [false, '{"foo":"bar",}'], + [false, '{"foo"}'], + [true, '["foo"]'], + [false, '{"foo": "bar"]'], + [true, '123', 'UTF-8'], + [true, '{"fòô": "bàř"}', 'UTF-8'], + [false, '{"fòô":"bàř",}', 'UTF-8'], + [false, '{"fòô"}', 'UTF-8'], + [false, '["fòô": "bàř"]', 'UTF-8'], + [true, '["fòô"]', 'UTF-8'], + [false, '{"fòô": "bàř"]', 'UTF-8'], + ]; + foreach($data as $value) { + @list($expected, $str, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->isJson(); + + \expect($result)->toBeA('boolean'); + \expect((string) $stringy)->toBe($str); + \expect((bool) $result)->toBe($expected); + } + + }); + + context('test isSerialized', function () { + $data = [ + [false, ''], + [true, 'a:1:{s:3:"foo";s:3:"bar";}'], + [false, 'a:1:{s:3:"foo";s:3:"bar"}'], + [true, serialize(['foo' => 'bar'])], + [true, 'a:1:{s:5:"fòô";s:5:"bàř";}', 'UTF-8'], + [false, 'a:1:{s:5:"fòô";s:5:"bàř"}', 'UTF-8'], + [true, serialize(['fòô' => 'bár']), 'UTF-8'], + ]; + foreach($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + @list($expected, $str, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->isSerialized(); + + \expect($result)->toBeA('boolean'); + \expect((string) $stringy)->toBe($str); + \expect((bool) $result)->toBe($expected); + }); + } + }); + + context('test isBase64', function () { + $data = [ + [false, ' '], + [true, ''], + [true, base64_encode('FooBar') ], + [true, base64_encode(' ') ], + [true, base64_encode('FÒÔBÀŘ') ], + [true, base64_encode('συγγραφέας') ], + [false, 'Foobar'], + ]; + + foreach($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + list($expected, $str) = $value; + + $stringy = __('Stringy')::create($str); + $result = $stringy->isBase64(); + + \expect($result)->toBeA('boolean'); + \expect((string) $stringy)->toBe($str); + \expect((bool) $result)->toBe($expected); + }); + } + }); + + context('test longestCommonPrefix', function () { + $data = [ + ['foo', 'foobar', 'foo bar'], + ['foo bar', 'foo bar', 'foo bar'], + ['f', 'foo bar', 'far boo'], + ['', 'toy car', 'foo bar'], + ['', 'foo bar', ''], + ['fòô', 'fòôbar', 'fòô bar', 'UTF-8'], + ['fòô bar', 'fòô bar', 'fòô bar', 'UTF-8'], + ['fò', 'fòô bar', 'fòr bar', 'UTF-8'], + ['', 'toy car', 'fòô bar', 'UTF-8'], + ['', 'fòô bar', '', 'UTF-8'], + ]; + + foreach($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + @list($expected, $str, $otherStr, $encoding) = $value; + + $stringy = __('Stringy')::create($str); + $result = $stringy->longestCommonPrefix($otherStr); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + }); + } + }); + + context('test longestCommonSuffix', function () { + $data = [ + ['bar', 'foobar', 'foo bar'], + ['foo bar', 'foo bar', 'foo bar'], + ['ar', 'foo bar', 'boo far'], + ['', 'foo bad', 'foo bar'], + ['', 'foo bar', ''], + ['bàř', 'fòôbàř', 'fòô bàř', 'UTF-8'], + ['fòô bàř', 'fòô bàř', 'fòô bàř', 'UTF-8'], + [' bàř', 'fòô bàř', 'fòr bàř', 'UTF-8'], + ['', 'toy car', 'fòô bàř', 'UTF-8'], + ['', 'fòô bàř', '', 'UTF-8'], + ]; + + foreach($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + @list($expected, $str, $otherStr, $encoding) = $value; + + $stringy = __('Stringy')::create($str); + $result = $stringy->longestCommonSuffix($otherStr); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + }); + } + }); + + xcontext('test longestCommonSubstring', function () { + $data = [ + // ['ff', 'aff', 'ff'], + // ['foo', 'foobar', 'foo bar'], + // ['foo bar', 'foo bar', 'foo bar'], + // ['oo ', 'foo bar', 'boo far'], + // ['foo ba', 'foo bad', 'foo bar'], + // ['', 'foo bar', ''], + // ['fòô', 'fòôbàř', 'fòô bàř', 'UTF-8'], + // ['fòô bàř', 'fòô bàř', 'fòô bàř', 'UTF-8'], + [' bàř', 'fòô bàř', 'fòr bàř', 'UTF-8'], + [' ', 'toy car', 'fòô bàř', 'UTF-8'], + ['', 'fòô bàř', '', 'UTF-8'], + ]; + + foreach($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + @list($expected, $str, $otherStr, $encoding) = $value; + + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->longestCommonSubstring($otherStr); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + }); + } + }); + + context('test pad', function () { + $data = [ + // length <= str + ['foo bar', 'foo bar', -1], + ['foo bar', 'foo bar', 7], + ['fòô bàř', 'fòô bàř', 7, ' ', 'right', 'UTF-8'], + // right + ['foo bar ', 'foo bar', 9], + ['foo bar_*', 'foo bar', 9, '_*', 'right'], + ['fòô bàř¬ø¬', 'fòô bàř', 10, '¬ø', 'right', 'UTF-8'], + // left + [' foo bar', 'foo bar', 9, ' ', 'left'], + ['_*foo bar', 'foo bar', 9, '_*', 'left'], + ['¬ø¬fòô bàř', 'fòô bàř', 10, '¬ø', 'left', 'UTF-8'], + // both + ['foo bar ', 'foo bar', 8, ' ', 'both'], + ['¬fòô bàř¬ø', 'fòô bàř', 10, '¬ø', 'both', 'UTF-8'], + ['¬øfòô bàř¬øÿ', 'fòô bàř', 12, '¬øÿ', 'both', 'UTF-8'] + ]; + + foreach($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + @list($expected, $str, $length, $padStr, $padType, $encoding) = $value; + + $padType = $padType ?? 'right'; + + $padStr = $padStr ?? ' '; + + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->pad($length, $padStr, $padType); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + }); + } + }); + + context('test padLeft', function () { + $data = [ + [' foo bar', 'foo bar', 9], + ['_*foo bar', 'foo bar', 9, '_*'], + ['_*_foo bar', 'foo bar', 10, '_*'], + [' fòô bàř', 'fòô bàř', 9, ' ', 'UTF-8'], + ['¬øfòô bàř', 'fòô bàř', 9, '¬ø', 'UTF-8'], + ['¬ø¬fòô bàř', 'fòô bàř', 10, '¬ø', 'UTF-8'], + ['¬ø¬øfòô bàř', 'fòô bàř', 11, '¬ø', 'UTF-8'], + ]; + + foreach($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + @list($expected, $str, $length, $padStr, $encoding) = $value; + + $padStr = $padStr ?? ' '; + + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->padLeft($length, $padStr); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + }); + } + }); + context('test padRight', function () { + $data = [ + ['foo bar ', 'foo bar', 9], + ['foo bar_*', 'foo bar', 9, '_*'], + ['foo bar_*_', 'foo bar', 10, '_*'], + ['fòô bàř ', 'fòô bàř', 9, ' ', 'UTF-8'], + ['fòô bàř¬ø', 'fòô bàř', 9, '¬ø', 'UTF-8'], + ['fòô bàř¬ø¬', 'fòô bàř', 10, '¬ø', 'UTF-8'], + ['fòô bàř¬ø¬ø', 'fòô bàř', 11, '¬ø', 'UTF-8'], + ]; + + foreach($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + @list($expected, $str, $length, $padStr, $encoding) = $value; + + $padStr = $padStr ?? ' '; + + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->padRight($length, $padStr); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + }); + } + }); + context('test padBoth', function () { + $data = [ + ['foo bar ', 'foo bar', 8], + [' foo bar ', 'foo bar', 9, ' '], + ['fòô bàř ', 'fòô bàř', 8, ' ', 'UTF-8'], + [' fòô bàř ', 'fòô bàř', 9, ' ', 'UTF-8'], + ['fòô bàř¬', 'fòô bàř', 8, '¬ø', 'UTF-8'], + ['¬fòô bàř¬', 'fòô bàř', 9, '¬ø', 'UTF-8'], + ['¬fòô bàř¬ø', 'fòô bàř', 10, '¬ø', 'UTF-8'], + ['¬øfòô bàř¬ø', 'fòô bàř', 11, '¬ø', 'UTF-8'], + ['¬fòô bàř¬ø', 'fòô bàř', 10, '¬øÿ', 'UTF-8'], + ['¬øfòô bàř¬ø', 'fòô bàř', 11, '¬øÿ', 'UTF-8'], + ['¬øfòô bàř¬øÿ', 'fòô bàř', 12, '¬øÿ', 'UTF-8'] + ]; + + foreach($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + @list($expected, $str, $length, $padStr, $encoding) = $value; + + $padStr = $padStr ?? ' '; + + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->padBoth($length, $padStr); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + }); + } + }); + + context('test repeat', function () { + $data = [ + ['', 'foo', 0], + ['foo', 'foo', 1], + ['foofoo', 'foo', 2], + ['foofoofoo', 'foo', 3], + ['fòô', 'fòô', 1, 'UTF-8'], + ['fòôfòô', 'fòô', 2, 'UTF-8'], + ['fòôfòôfòô', 'fòô', 3, 'UTF-8'] + ]; + foreach ($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + @list($expected, $str, $multiplier, $encoding) = $value; + + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->repeat($multiplier); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + }); + } + }); + + context('test replace', function () { + $data = [ + ['', '', '', ''], + ['foo', '', '', 'foo'], + ['foo', '\s', '\s', 'foo'], + ['foo bar', 'foo bar', '', ''], + ['foo bar', 'foo bar', 'f(o)o', '\1'], + ['\1 bar', 'foo bar', 'foo', '\1'], + ['bar', 'foo bar', 'foo ', ''], + ['far bar', 'foo bar', 'foo', 'far'], + ['bar bar', 'foo bar foo bar', 'foo ', ''], + ['', '', '', '', 'UTF-8'], + ['fòô', '', '', 'fòô', 'UTF-8'], + ['fòô', '\s', '\s', 'fòô', 'UTF-8'], + ['fòô bàř', 'fòô bàř', '', '', 'UTF-8'], + ['bàř', 'fòô bàř', 'fòô ', '', 'UTF-8'], + ['far bàř', 'fòô bàř', 'fòô', 'far', 'UTF-8'], + ['bàř bàř', 'fòô bàř fòô bàř', 'fòô ', '', 'UTF-8'], + ]; + + foreach($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + @list($expected, $str, $search, $replacement, $encoding) = $value; + + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->replace($search, $replacement); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + }); + } + }); + + context('test toAsiic', function () { + $data = [ + ['foo bar', 'fòô bàř'], + [' TEST ', ' ŤÉŚŢ '], + ['f = z = 3', 'φ = ź = 3'], + ['perevirka', 'перевірка'], + ['lysaya gora', 'лысая гора'], + ['user@host', 'user@host'], + ['shchuka', 'щука'], + ['', '漢字'], + ['xin chao the gioi', 'xin chào thế giới'], + ['XIN CHAO THE GIOI', 'XIN CHÀO THẾ GIỚI'], + ['dam phat chet luon', 'đấm phát chết luôn'], + [' ', ' '], // no-break space (U+00A0) + [' ', '           '], // spaces U+2000 to U+200A + [' ', ' '], // narrow no-break space (U+202F) + [' ', ' '], // medium mathematical space (U+205F) + [' ', ' '], // ideographic space (U+3000) + ['', '𐍉'], // some uncommon, unsupported character (U+10349) + ['𐍉', '𐍉', 'en', false], + ['aouAOU', 'äöüÄÖÜ'], + ['aeoeueAEOEUE', 'äöüÄÖÜ', 'de'], + ['aeoeueAEOEUE', 'äöüÄÖÜ', 'de_DE'] + ]; + + foreach ($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + @list($expected, $str, $language, $removeUnsupported) = $value; + $language = $language ?? 'en'; + $removeUnsupported = $removeUnsupported ?? true; + + $stringy = __('Stringy')::create($str); + $result = $stringy->toAscii($language, $removeUnsupported); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + }); + } + }); + + context('test toSpaces', function () { + $data = [ + [' foo bar ', ' foo bar '], + [' foo bar ', ' foo bar ', 5], + [' foo bar ', ' foo bar ', 2], + ['foobar', ' foo bar ', 0], + [" foo\n bar", " foo\n bar"], + [" fòô\n bàř", " fòô\n bàř"] + ]; + + foreach ($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + @list($expected, $str, $tabLength) = $value; + $tabLength = $tabLength ?? 4; + + $stringy = __('Stringy')::create($str); + $result = $stringy->toSpaces($tabLength); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + }); + } + }); + + context('test underscored', function () { + $data = [ + ['test_case', 'testCase'], + ['test_case', 'Test-Case'], + ['test_case', 'test case'], + ['test_case', 'test -case'], + ['_test_case', '-test - case'], + ['test_case', 'test_case'], + ['test_c_test', ' test c test'], + ['test_u_case', 'TestUCase'], + ['test_c_c_test', 'TestCCTest'], + ['string_with1number', 'string_with1number'], + ['string_with_2_2_numbers', 'String-with_2_2 numbers'], + ['1test2case', '1test2case'], + ['yes_we_can', 'yesWeCan'], + ['test_σase', 'test Σase', 'UTF-8'], + ['στανιλ_case', 'Στανιλ case', 'UTF-8'], + ['σash_case', 'Σash Case', 'UTF-8'] + ]; + + foreach ($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + @list($expected, $str, $encoding) = $value; + + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->underscored(); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + }); + } + }); + + context('test toUpperCase', function () { + $data = [ + ['FOO BAR', 'foo bar'], + [' FOO_BAR ', ' FOO_bar '], + ['FÒÔ BÀŘ', 'fòô bàř', 'UTF-8'], + [' FÒÔ_BÀŘ ', ' FÒÔ_bàř ', 'UTF-8'], + ['ΑΥΤΟΚΊΝΗΤΟ', 'αυτοκίνητο', 'UTF-8'], + ]; + + foreach ($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + @list($expected, $str, $encoding) = $value; + + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->toUpperCase(); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + }); + } + }); + + context('test truncate', function () { + $data = [ + ['Test foo bar', 'Test foo bar', 12], + ['Test foo ba', 'Test foo bar', 11], + ['Test foo', 'Test foo bar', 8], + ['Test fo', 'Test foo bar', 7], + ['Test', 'Test foo bar', 4], + ['Test foo bar', 'Test foo bar', 12, '...'], + ['Test foo...', 'Test foo bar', 11, '...'], + ['Test ...', 'Test foo bar', 8, '...'], + ['Test...', 'Test foo bar', 7, '...'], + ['T...', 'Test foo bar', 4, '...'], + ['Test fo....', 'Test foo bar', 11, '....'], + ['Test fòô bàř', 'Test fòô bàř', 12, '', 'UTF-8'], + ['Test fòô bà', 'Test fòô bàř', 11, '', 'UTF-8'], + ['Test fòô', 'Test fòô bàř', 8, '', 'UTF-8'], + ['Test fò', 'Test fòô bàř', 7, '', 'UTF-8'], + ['Test', 'Test fòô bàř', 4, '', 'UTF-8'], + ['Test fòô bàř', 'Test fòô bàř', 12, 'ϰϰ', 'UTF-8'], + ['Test fòô ϰϰ', 'Test fòô bàř', 11, 'ϰϰ', 'UTF-8'], + ['Test fϰϰ', 'Test fòô bàř', 8, 'ϰϰ', 'UTF-8'], + ['Test ϰϰ', 'Test fòô bàř', 7, 'ϰϰ', 'UTF-8'], + ['Teϰϰ', 'Test fòô bàř', 4, 'ϰϰ', 'UTF-8'], + ['What are your pl...', 'What are your plans today?', 19, '...'] + ]; + + foreach ($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + @list($expected, $str, $length, $substring, $encoding) = $value; + + $substring = $substring ?? ''; + + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->truncate($length, $substring); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + }); + } + }); + + context('test toTitleCase', function () { + $data = [ + ['Foo Bar', 'foo bar'], + [' Foo_Bar ', ' foo_bar '], + ['Fòô Bàř', 'fòô bàř', 'UTF-8'], + [' Fòô_Bàř ', ' fòô_bàř ', 'UTF-8'], + ['Αυτοκίνητο Αυτοκίνητο', 'αυτοκίνητο αυτοκίνητο', 'UTF-8'], + ]; + + foreach ($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + @list($expected, $str, $encoding) = $value; + + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->toTitleCase(); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + }); + } + }); + + context('test toTabs', function () { + $data = [ + [' foo bar ', ' foo bar '], + [' foo bar ', ' foo bar ', 5], + [' foo bar ', ' foo bar ', 2], + [" foo\n bar", " foo\n bar"], + [" fòô\n bàř", " fòô\n bàř"] + ]; + + foreach ($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + @list($expected, $str, $tabLength) = $value; + $tabLength = $tabLength ?? 4; + + $stringy = __('Stringy')::create($str); + $result = $stringy->toTabs($tabLength); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + }); + } + }); + + context('test toLowerCase', function () { + $data = [ + ['foo bar', 'FOO BAR'], + [' foo_bar ', ' FOO_bar '], + ['fòô bàř', 'FÒÔ BÀŘ', 'UTF-8'], + [' fòô_bàř ', ' FÒÔ_bàř ', 'UTF-8'], + ['αυτοκίνητο', 'ΑΥΤΟΚΊΝΗΤΟ', 'UTF-8'], + ]; + + foreach ($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + @list($expected, $str, $tabLength) = $value; + $tabLength = $tabLength ?? 4; + + $stringy = __('Stringy')::create($str); + $result = $stringy->toLowerCase($tabLength); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + }); + } + }); + + context('test toBoolean', function () { + $data = [ + [true, 'true'], + [true, '1'], + [true, 'on'], + [true, 'ON'], + [true, 'yes'], + [true, '999'], + [false, 'false'], + [false, '0'], + [false, 'off'], + [false, 'OFF'], + [false, 'no'], + [false, '-999'], + [false, ''], + [false, ' '], + [false, '  ', 'UTF-8'] // narrow no-break space (U+202F) + ]; + + foreach ($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + @list($expected, $str, $encoding) = $value; + + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->toBoolean(); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((bool) $result)->toBe($expected); + }); + } + }); + + context('test titleize', function () { + $ignore = ['at', 'by', 'for', 'in', 'of', 'on', 'out', 'to', 'the']; + + $data = [ + ['Title Case', 'TITLE CASE'], + ['Testing The Method', 'testing the method'], + ['Testing the Method', 'testing the method', $ignore], + ['I Like to Watch Dvds at Home', 'i like to watch DVDs at home', + $ignore], + ['Θα Ήθελα Να Φύγει', ' Θα ήθελα να φύγει ', null, 'UTF-8'] + ]; + + foreach ($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + @list($expected, $str, $ignore, $encoding) = $value; + + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->titleize($ignore); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + }); + } + }); + + context('test reverse', function () { + $data = [ + ['', ''], + ['raboof', 'foobar'], + ['řàbôòf', 'fòôbàř', 'UTF-8'], + ['řàb ôòf', 'fòô bàř', 'UTF-8'], + ['∂∆ ˚åß', 'ßå˚ ∆∂', 'UTF-8'] + ]; + + foreach ($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + + @list($str, $expected, $encoding) = $value; + + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->reverse(); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + }); + } + }); + + context('test safeTruncate', function () { + $data = [ + ['Test foo bar', 'Test foo bar', 12], + ['Test foo', 'Test foo bar', 11], + ['Test foo', 'Test foo bar', 8], + ['Test', 'Test foo bar', 7], + ['Test', 'Test foo bar', 4], + ['Test foo bar', 'Test foo bar', 12, '...'], + ['Test foo...', 'Test foo bar', 11, '...'], + ['Test...', 'Test foo bar', 8, '...'], + ['Test...', 'Test foo bar', 7, '...'], + ['T...', 'Test foo bar', 4, '...'], + ['Test....', 'Test foo bar', 11, '....'], + ['Tëst fòô bàř', 'Tëst fòô bàř', 12, '', 'UTF-8'], + ['Tëst fòô', 'Tëst fòô bàř', 11, '', 'UTF-8'], + ['Tëst fòô', 'Tëst fòô bàř', 8, '', 'UTF-8'], + ['Tëst', 'Tëst fòô bàř', 7, '', 'UTF-8'], + ['Tëst', 'Tëst fòô bàř', 4, '', 'UTF-8'], + ['Tëst fòô bàř', 'Tëst fòô bàř', 12, 'ϰϰ', 'UTF-8'], + ['Tëst fòôϰϰ', 'Tëst fòô bàř', 11, 'ϰϰ', 'UTF-8'], + ['Tëstϰϰ', 'Tëst fòô bàř', 8, 'ϰϰ', 'UTF-8'], + ['Tëstϰϰ', 'Tëst fòô bàř', 7, 'ϰϰ', 'UTF-8'], + ['Tëϰϰ', 'Tëst fòô bàř', 4, 'ϰϰ', 'UTF-8'], + ['What are your plans...', 'What are your plans today?', 22, '...'] + ]; + + foreach ($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + + @list($expected, $str, $length, $substring, $encoding) = $value; + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->safeTruncate($length, $substring); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect((string) $result)->toBe($expected); + }); + } + }); + + context('test shuffle', function () { + $data = [ + ['foo bar'], + ['∂∆ ˚åß', 'UTF-8'], + ['å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', 'UTF-8'] + ]; + + foreach ($data as $key => $value) { + it(__formatMessage($key, $value), function () use ($value) { + + @list($str, $encoding) = $value; + $encoding = $encoding ?: mb_internal_encoding(); + $stringy = __('Stringy')::create($str, $encoding); + $result = $stringy->shuffle(); + + \expect($stringy)->toBeAnInstanceOf(__('Stringy')); + \expect((string) $stringy)->toBe($str); + \expect(mb_strlen($str, $encoding))->toBe(mb_strlen((string) $result, $encoding)); + + for ($i = 0; $i < mb_strlen($str, $encoding); $i++) { + $char = mb_substr($str, $i, 1, $encoding); + $countBefore = mb_substr_count($str, $char, $encoding); + $countAfter = mb_substr_count($result, $char, $encoding); + \expect($countBefore)->toBe($countAfter); + } + }); + } + }); +}); diff --git a/spec/TestSpec.php b/spec/TestSpec.php index d76606c..e3e5876 100644 --- a/spec/TestSpec.php +++ b/spec/TestSpec.php @@ -1,34 +1,34 @@ toEcho('hello world!'); + expect([_('Test'), 'helloWorld'])->toEcho('hello world!'); }); it('测试实例化 hello world!!', function () { - $instance = new Test(); - expect([$instance, 'echoHelloWorld'])->toEcho('hello world!!'); + expect([_('Test'), 'echoHelloWorld'])->toEcho('hello world!!'); }); it('测试 public 属性', function () { - $instance = new Test(); - expect($instance->publicProperty)->toBe('hello world +property'); + expect(_('Test')->publicProperty)->toBe('hello world +property'); }); it('测试 public static 属性', function () { - expect(Test::$publicPropertyStatic)->toBe('hello world +property +static'); + expect(__('Test')::$publicPropertyStatic)->toBe('hello world +property +static'); }); it('测试 const 属性', function () { - expect(Test::PUBLIC_CONST)->toBe('hello world +const'); + expect(__('Test')::PUBLIC_CONST)->toBe('hello world +const'); }); it('测试方法不存在的逻辑', function () { - $instance = new Test(); + $instance = _('Test'); $params = [ 'a21212' => [1, 2, 3, 4], '😊☺😀' => ['abab', 'cdcd', 'efef'], diff --git a/spec/const.php b/spec/const.php new file mode 100644 index 0000000..ae4c4d5 --- /dev/null +++ b/spec/const.php @@ -0,0 +1,9 @@ + %s <==', $message, json_encode($data, JSON_UNESCAPED_UNICODE|JSON_INVALID_UTF8_IGNORE)); +} + +function __debug_array(array $arr): string +{ + $i = $j = 0; + + $ret = ''; + for ($i = 0; $i < count($arr); $i++) { + for ($j = 0; $j < count($arr[$i]); $j++) { + $ret .= " ($i, $j) "; + } + $ret .= "\n"; + } + $ret .= "\n"; + + return $ret; +} diff --git a/src/Minbaby/Php/Stringy/Stringy.php b/src/Minbaby/Php/Stringy/Stringy.php new file mode 100644 index 0000000..4c6cac0 --- /dev/null +++ b/src/Minbaby/Php/Stringy/Stringy.php @@ -0,0 +1,1104 @@ +str = (string) $str; + $this->encoding = $encoding ?: \mb_internal_encoding(); + } + + public function __toString() + { + return $this->str; + } + + public function getEncoding() + { + return $this->encoding; + } + + public static function create($str = '', $encoding = null) + { + return new static($str, $encoding); + } + + public function collapseWhiteSpace() + { + return $this->regexReplace('[[:space:]]+', ' ')->trim(); + } + + public function regexReplace($pattern, $replacement, $options = 'msr') + { + $regexEncoding = $this->regexEncoding(); + $this->regexEncoding($this->encoding); + + $str = $this->eregReplace($pattern, $replacement, $this->str, $options); + $this->regexEncoding($regexEncoding); + + return static::create($str, $this->encoding); + } + + protected function regexEncoding() + { + static $functionExists; + + if ($functionExists === null) { + $functionExists = function_exists('\mb_regex_encoding'); + } + + if ($functionExists) { + $args = func_get_args(); + + return \call_user_func_array('\mb_regex_encoding', $args); + } + } + + protected function eregReplace($pattern, $replacement, $string, $option = 'msr') + { + static $functionExists; + if ($functionExists === null) { + $functionExists = function_exists('\mb_split'); + } + if ($functionExists) { + return \mb_ereg_replace($pattern, $replacement, $string, $option); + } elseif ($this->supportsEncoding()) { + $option = str_replace('r', '', $option); + + return \preg_replace("/$pattern/u$option", $replacement, $string); + } + } + + public function trim($chars = null) + { + $chars = ($chars) ? preg_quote($chars) : '[:space:]'; + + return $this->regexReplace("^[$chars]+|[$chars]+\$", ''); + } + + public function swapCase() + { + $stringy = static::create($this->str, $this->encoding); + $encoding = $stringy->encoding; + $stringy->str = preg_replace_callback( + '/[\S]/u', + function ($match) use ($encoding) { + if ($match[0] == \mb_strtoupper($match[0], $encoding)) { + return \mb_strtolower($match[0], $encoding); + } + + return \mb_strtoupper($match[0], $encoding); + }, + $stringy->str + ); + + return $stringy; + } + + public function upperCaseFirst() + { + $first = \mb_substr($this->str, 0, 1, $this->encoding); + $rest = \mb_substr($this->str, 1, $this->length() - 1, $this->encoding); + $str = \mb_strtoupper($first, $this->encoding).$rest; + + return static::create($str, $this->encoding); + } + + public function length() + { + return \mb_strlen($this->str, $this->encoding); + } + + public function count() + { + return $this->length(); + } + + public function getIterator() + { + return new \ArrayIterator($this->chars()); + } + + public function chars() + { + $chars = []; + for ($i = 0, $l = $this->length(); $i < $l; $i++) { + $chars[] = $this->at($i)->str; + } + + return $chars; + } + + public function at($index) + { + return $this->substr($index, 1); + } + + public function substr($start, $length = null) + { + $length = $length === null ? $this->length() : $length; + $str = \mb_substr($this->str, $start, $length, $this->encoding); + + return static::create($str, $this->encoding); + } + + public function offsetExists($offset) + { + $length = $this->length(); + $offset = (int) $offset; + + if ($offset >= 0) { + return $length > $offset; + } + + return $length >= abs($offset); + } + + public function offsetGet($offset) + { + $offset = (int) $offset; + $length = $this->length(); + if (($offset >= 0 && $length <= $offset) || $length < abs($offset)) { + throw new \OutOfBoundsException('No character exists at the index'); + } + + return \mb_substr($this->str, $offset, 1, $this->encoding); + } + + public function offsetSet($offset, $value) + { + throw new \Exception('Stringy object is immutable, cannot modify char'); + } + + public function offsetUnset($offset) + { + throw new \Exception('Stringy object is immutable, cannot unset char'); + } + + public function indexOf($needle, $offset = 0) + { + return \mb_strpos( + $this->str, + (string) $needle, + (int) $offset, + $this->encoding + ); + } + + public function indexOfLast($needle, $offset = 0) + { + return \mb_strrpos( + $this->str, + (string) $needle, + (int) $offset, + $this->encoding + ); + } + + public function append($string) + { + return static::create($this->str.$string, $this->encoding); + } + + public function prepend($string) + { + return static::create($string.$this->str, $this->encoding); + } + + public function lines() + { + $array = $this->split('[\r\n]{1,2}', $this->str); + for ($i = 0; $i < count($array); $i++) { + $array[$i] = static::create($array[$i], $this->encoding); + } + + return $array; + } + + public function split($pattern, $limit = null) + { + if ($limit === 0) { + return []; + } + // mb_split errors when supplied an empty pattern in < PHP 5.4.13 + // and HHVM < 3.8 + if ($pattern === '') { + return [static::create($this->str, $this->encoding)]; + } + $regexEncoding = $this->regexEncoding(); + $this->regexEncoding($this->encoding); + // mb_split returns the remaining unsplit string in the last index when + // supplying a limit + $limit = ($limit > 0) ? $limit += 1 : -1; + static $functionExists; + if ($functionExists === null) { + $functionExists = function_exists('\mb_split'); + } + if ($functionExists) { + $array = \mb_split($pattern, $this->str, $limit); + } elseif ($this->supportsEncoding()) { + $array = \preg_split("/$pattern/", $this->str, $limit); + } + $this->regexEncoding($regexEncoding); + if ($limit > 0 && count($array) === $limit) { + array_pop($array); + } + for ($i = 0; $i < count($array); $i++) { + $array[$i] = static::create($array[$i], $this->encoding); + } + + return $array; + } + + public function lowerCaseFirst() + { + $first = \mb_substr($this->str, 0, 1, $this->encoding); + $rest = \mb_substr($this->str, 1, $this->length() - 1, $this->encoding); + $str = \mb_strtolower($first, $this->encoding).$rest; + + return static::create($str, $this->encoding); + } + + public function camelize() + { + $encoding = $this->encoding; + $stringy = $this->trim()->lowerCaseFirst(); + $stringy->str = preg_replace('/^[-_]+/', '', $stringy->str); + $stringy->str = preg_replace_callback( + '/[-_\s]+(.)?/u', + function ($match) use ($encoding) { + if (isset($match[1])) { + return \mb_strtoupper($match[1], $encoding); + } + + return ''; + }, + $stringy->str + ); + $stringy->str = preg_replace_callback( + '/[\d]+(.)?/u', + function ($match) use ($encoding) { + return \mb_strtoupper($match[0], $encoding); + }, + $stringy->str + ); + + return $stringy; + } + + public function trimLeft($chars = null) + { + $chars = ($chars) ? preg_quote($chars) : '[:space:]'; + + return $this->regexReplace("^[$chars]+", ''); + } + + public function trimRight($chars = null) + { + $chars = ($chars) ? preg_quote($chars) : '[:space:]'; + + return $this->regexReplace("[$chars]+\$", ''); + } + + public function between($start, $end, $offset = 0) + { + $startIndex = $this->indexOf($start, $offset); + if ($startIndex === false) { + return static::create('', $this->encoding); + } + $substrIndex = $startIndex + \mb_strlen($start, $this->encoding); + $endIndex = $this->indexOf($end, $substrIndex); + if ($endIndex === false) { + return static::create('', $this->encoding); + } + + return $this->substr($substrIndex, $endIndex - $substrIndex); + } + + public function contains($needle, $caseSensitive = true) + { + $encoding = $this->encoding; + if ($caseSensitive) { + return \mb_strpos($this->str, $needle, 0, $encoding) !== false; + } + + return \mb_stripos($this->str, $needle, 0, $encoding) !== false; + } + + public function containsAll($needles, $caseSensitive = true) + { + if (empty($needles)) { + return false; + } + foreach ($needles as $needle) { + if (!$this->contains($needle, $caseSensitive)) { + return false; + } + } + + return true; + } + + public function containsAny($needles, $caseSensitive = true) + { + if (empty($needles)) { + return false; + } + foreach ($needles as $needle) { + if ($this->contains($needle, $caseSensitive)) { + return true; + } + } + + return false; + } + + public function countSubstr($substring, $caseSensitive = true) + { + if ($caseSensitive) { + return \mb_substr_count($this->str, $substring, $this->encoding); + } + $str = \mb_strtoupper($this->str, $this->encoding); + $substring = \mb_strtoupper($substring, $this->encoding); + + return \mb_substr_count($str, $substring, $this->encoding); + } + + public function delimit($delimiter) + { + $regexEncoding = $this->regexEncoding(); + $this->regexEncoding($this->encoding); + $str = $this->eregReplace('\B([A-Z])', '-\1', $this->trim()); + $str = \mb_strtolower($str, $this->encoding); + $str = $this->eregReplace('[-_\s]+', $delimiter, $str); + $this->regexEncoding($regexEncoding); + + return static::create($str, $this->encoding); + } + + public function dasherize() + { + return $this->delimit('-'); + } + + // protected function supportsEncoding() + // { + // $supported = ['UTF-8' => true, 'ASCII' => true]; + // if (isset($supported[$this->encoding])) { + // return true; + // } else { + // throw new \RuntimeException('Stringy method requires the ' . + // 'mbstring module for encodings other than ASCII and UTF-8. ' . + // 'Encoding used: ' . $this->encoding); + // } + // } + + public function endsWith($substring, $caseSensitive = true) + { + $substringLength = \mb_strlen($substring, $this->encoding); + $strLength = $this->length(); + $endOfStr = \mb_substr($this->str, $strLength - $substringLength, $substringLength, $this->encoding); + if (!$caseSensitive) { + $substring = \mb_strtolower($substring, $this->encoding); + $endOfStr = \mb_strtolower($endOfStr, $this->encoding); + } + + return (string) $substring === $endOfStr; + } + + public function endsWithAny($substrings, $caseSensitive = true) + { + if (empty($substrings)) { + return false; + } + foreach ($substrings as $substring) { + if ($this->endsWith($substring, $caseSensitive)) { + return true; + } + } + + return false; + } + + public function ensureLeft($substring) + { + $stringy = static::create($this->str, $this->encoding); + if (!$stringy->startsWith($substring)) { + $stringy->str = $substring.$stringy->str; + } + + return $stringy; + } + + public function startsWith($substring, $caseSensitive = true) + { + $substringLength = \mb_strlen($substring, $this->encoding); + $startOfStr = \mb_substr($this->str, 0, $substringLength, $this->encoding); + if (!$caseSensitive) { + $substring = \mb_strtolower($substring, $this->encoding); + $startOfStr = \mb_strtolower($startOfStr, $this->encoding); + } + + return (string) $substring === $startOfStr; + } + + public function ensureRight($substring) + { + $stringy = static::create($this->str, $this->encoding); + if (!$stringy->endsWith($substring)) { + $stringy->str .= $substring; + } + + return $stringy; + } + + public function first($n) + { + $stringy = static::create($this->str, $this->encoding); + if ($n < 0) { + $stringy->str = ''; + + return $stringy; + } + + return $stringy->substr(0, $n); + } + + protected function matchesPattern($pattern) + { + $regexEncoding = $this->regexEncoding(); + $this->regexEncoding($this->encoding); + $match = \mb_ereg_match($pattern, $this->str); + $this->regexEncoding($regexEncoding); + + return $match; + } + + public function isAlpha() + { + return $this->matchesPattern('^[[:alpha:]]*$'); + } + + public function isBlank() + { + return $this->matchesPattern('^[[:space:]]*$'); + } + + public function hasLowerCase() + { + return $this->matchesPattern('.*[[:lower:]]'); + } + + public function hasUpperCase() + { + return $this->matchesPattern('.*[[:upper:]]'); + } + + public function htmlDecode($flags = ENT_COMPAT) + { + $str = html_entity_decode($this->str, $flags, $this->encoding); + return static::create($str, $this->encoding); + } + + public function htmlEncode($flags = ENT_COMPAT) + { + $str = htmlentities($this->str, $flags, $this->encoding); + return static::create($str, $this->encoding); + } + + public function removeLeft($substring) + { + $stringy = static::create($this->str, $this->encoding); + if ($stringy->startsWith($substring)) { + $substringLength = \mb_strlen($substring, $stringy->encoding); + return $stringy->substr($substringLength); + } + return $stringy; + } + + public function removeRight($substring) + { + $stringy = static::create($this->str, $this->encoding); + if ($stringy->endsWith($substring)) { + $substringLength = \mb_strlen($substring, $stringy->encoding); + return $stringy->substr(0, $stringy->length() - $substringLength); + } + return $stringy; + } + + public function last($n) + { + $stringy = static::create($this->str, $this->encoding); + if ($n <= 0) { + $stringy->str = ''; + return $stringy; + } + return $stringy->substr(-$n); + } + + public function humanize() + { + $str = str_replace(['_id', '_'], ['', ' '], $this->str); + return static::create($str, $this->encoding)->trim()->upperCaseFirst(); + } + + public function insert($substring, $index) + { + $stringy = static::create($this->str, $this->encoding); + if ($index > $stringy->length()) { + return $stringy; + } + $start = \mb_substr($stringy->str, 0, $index, $stringy->encoding); + $end = \mb_substr($stringy->str, $index, $stringy->length(), $stringy->encoding); + $stringy->str = $start . $substring . $end; + return $stringy; + } + + public function isAlphanumeric() + { + return $this->matchesPattern('^[[:alnum:]]*$'); + } + + public function isHexadecimal() + { + return $this->matchesPattern('^[[:xdigit:]]*$'); + } + + public function isLowerCase() + { + return $this->matchesPattern('^[[:lower:]]*$'); + } + + public function isUpperCase() + { + return $this->matchesPattern('^[[:upper:]]*$'); + } + + public function isJson() + { + if (!$this->length()) { + return false; + } + json_decode($this->str); + return (json_last_error() === JSON_ERROR_NONE); + } + + public function isSerialized() + { + return $this->str === 'b:0;' || @unserialize($this->str) !== false; + } + + public function isBase64() + { + return (base64_encode(base64_decode($this->str, true)) === $this->str); + } + + public function longestCommonPrefix($otherStr) + { + $encoding = $this->encoding; + $maxLength = min($this->length(), \mb_strlen($otherStr, $encoding)); + $longestCommonPrefix = ''; + for ($i = 0; $i < $maxLength; $i++) { + $char = \mb_substr($this->str, $i, 1, $encoding); + if ($char == \mb_substr($otherStr, $i, 1, $encoding)) { + $longestCommonPrefix .= $char; + } else { + break; + } + } + return static::create($longestCommonPrefix, $encoding); + } + + public function longestCommonSuffix($otherStr) + { + $encoding = $this->encoding; + $maxLength = min($this->length(), \mb_strlen($otherStr, $encoding)); + $longestCommonSuffix = ''; + for ($i = 1; $i <= $maxLength; $i++) { + $char = \mb_substr($this->str, -$i, 1, $encoding); + if ($char == \mb_substr($otherStr, -$i, 1, $encoding)) { + $longestCommonSuffix = $char . $longestCommonSuffix; + } else { + break; + } + } + return static::create($longestCommonSuffix, $encoding); + } + + public function longestCommonSubstring($otherStr) + { + // Uses dynamic programming to solve + // http://en.wikipedia.org/wiki/Longest_common_substring_problem + $encoding = $this->encoding; + $stringy = static::create($this->str, $encoding); + $strLength = $stringy->length(); + $otherLength = \mb_strlen($otherStr, $encoding); + // Return if either string is empty + if ($strLength == 0 || $otherLength == 0) { + $stringy->str = ''; + return $stringy; + } + $len = 0; + $end = 0; + $table = array_fill(0, $strLength + 1, + array_fill(0, $otherLength + 1, 0)); + for ($i = 1; $i <= $strLength; $i++) { + for ($j = 1; $j <= $otherLength; $j++) { + $strChar = \mb_substr($stringy->str, $i - 1, 1, $encoding); + $otherChar = \mb_substr($otherStr, $j - 1, 1, $encoding); + if ($strChar == $otherChar) { + $table[$i][$j] = $table[$i - 1][$j - 1] + 1; + if ($table[$i][$j] > $len) { + $len = $table[$i][$j]; + $end = $i; + } + } else { + $table[$i][$j] = 0; + } + } + } + $stringy->str = \mb_substr($stringy->str, $end - $len, $len, $encoding); + return $stringy; + } + + public function pad($length, $padStr = ' ', $padType = 'right') + { + if (!in_array($padType, ['left', 'right', 'both'])) { + throw new InvalidArgumentException('Pad expects $padType ' . + "to be one of 'left', 'right' or 'both'"); + } + switch ($padType) { + case 'left': + return $this->padLeft($length, $padStr); + case 'right': + return $this->padRight($length, $padStr); + default: + return $this->padBoth($length, $padStr); + } + } + + public function padBoth($length, $padStr = ' ') + { + $padding = $length - $this->length(); + return $this->applyPadding(floor($padding / 2), ceil($padding / 2), + $padStr); + } + + public function padLeft($length, $padStr = ' ') + { + return $this->applyPadding($length - $this->length(), 0, $padStr); + } + + public function padRight($length, $padStr = ' ') + { + return $this->applyPadding(0, $length - $this->length(), $padStr); + } + + protected function applyPadding($left = 0, $right = 0, $padStr = ' ') + { + $stringy = static::create($this->str, $this->encoding); + $length = \mb_strlen($padStr, $stringy->encoding); + $strLength = $stringy->length(); + $paddedLength = $strLength + $left + $right; + if (!$length || $paddedLength <= $strLength) { + return $stringy; + } + $leftPadding = \mb_substr(str_repeat($padStr, ceil($left / $length)), 0, + $left, $stringy->encoding); + $rightPadding = \mb_substr(str_repeat($padStr, ceil($right / $length)), + 0, $right, $stringy->encoding); + $stringy->str = $leftPadding . $stringy->str . $rightPadding; + return $stringy; + } + + public function repeat($multiplier) + { + $repeated = str_repeat($this->str, $multiplier); + return static::create($repeated, $this->encoding); + } + + public function replace($search, $replacement) + { + return $this->regexReplace(preg_quote($search), $replacement); + } + + public function toAscii($language = 'en', $removeUnsupported = true) + { + $str = $this->str; + $langSpecific = $this->langSpecificCharsArray($language); + if (!empty($langSpecific)) { + $str = str_replace($langSpecific[0], $langSpecific[1], $str); + } + foreach ($this->charsArray() as $key => $value) { + $str = str_replace($value, $key, $str); + } + if ($removeUnsupported) { + $str = preg_replace('/[^\x20-\x7E]/u', '', $str); + } + return static::create($str, $this->encoding); + } + + protected static function langSpecificCharsArray($language = 'en') + { + $split = preg_split('/[-_]/', $language); + $language = strtolower($split[0]); + static $charsArray = []; + if (isset($charsArray[$language])) { + return $charsArray[$language]; + } + $languageSpecific = [ + 'de' => [ + ['ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü' ], + ['ae', 'oe', 'ue', 'AE', 'OE', 'UE'], + ], + 'bg' => [ + ['х', 'Х', 'щ', 'Щ', 'ъ', 'Ъ', 'ь', 'Ь'], + ['h', 'H', 'sht', 'SHT', 'a', 'А', 'y', 'Y'] + ] + ]; + if (isset($languageSpecific[$language])) { + $charsArray[$language] = $languageSpecific[$language]; + } else { + $charsArray[$language] = []; + } + return $charsArray[$language]; + } + + protected function charsArray() + { + static $charsArray; + if (isset($charsArray)) return $charsArray; + return $charsArray = [ + '0' => ['°', '₀', '۰', '0'], + '1' => ['¹', '₁', '۱', '1'], + '2' => ['²', '₂', '۲', '2'], + '3' => ['³', '₃', '۳', '3'], + '4' => ['⁴', '₄', '۴', '٤', '4'], + '5' => ['⁵', '₅', '۵', '٥', '5'], + '6' => ['⁶', '₆', '۶', '٦', '6'], + '7' => ['⁷', '₇', '۷', '7'], + '8' => ['⁸', '₈', '۸', '8'], + '9' => ['⁹', '₉', '۹', '9'], + 'a' => ['à', 'á', 'ả', 'ã', 'ạ', 'ă', 'ắ', 'ằ', 'ẳ', 'ẵ', + 'ặ', 'â', 'ấ', 'ầ', 'ẩ', 'ẫ', 'ậ', 'ā', 'ą', 'å', + 'α', 'ά', 'ἀ', 'ἁ', 'ἂ', 'ἃ', 'ἄ', 'ἅ', 'ἆ', 'ἇ', + 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ὰ', 'ά', + 'ᾰ', 'ᾱ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'а', 'أ', 'အ', + 'ာ', 'ါ', 'ǻ', 'ǎ', 'ª', 'ა', 'अ', 'ا', 'a', 'ä'], + 'b' => ['б', 'β', 'ب', 'ဗ', 'ბ', 'b'], + 'c' => ['ç', 'ć', 'č', 'ĉ', 'ċ', 'c'], + 'd' => ['ď', 'ð', 'đ', 'ƌ', 'ȡ', 'ɖ', 'ɗ', 'ᵭ', 'ᶁ', 'ᶑ', + 'д', 'δ', 'د', 'ض', 'ဍ', 'ဒ', 'დ', 'd'], + 'e' => ['é', 'è', 'ẻ', 'ẽ', 'ẹ', 'ê', 'ế', 'ề', 'ể', 'ễ', + 'ệ', 'ë', 'ē', 'ę', 'ě', 'ĕ', 'ė', 'ε', 'έ', 'ἐ', + 'ἑ', 'ἒ', 'ἓ', 'ἔ', 'ἕ', 'ὲ', 'έ', 'е', 'ё', 'э', + 'є', 'ə', 'ဧ', 'ေ', 'ဲ', 'ე', 'ए', 'إ', 'ئ', 'e'], + 'f' => ['ф', 'φ', 'ف', 'ƒ', 'ფ', 'f'], + 'g' => ['ĝ', 'ğ', 'ġ', 'ģ', 'г', 'ґ', 'γ', 'ဂ', 'გ', 'گ', + 'g'], + 'h' => ['ĥ', 'ħ', 'η', 'ή', 'ح', 'ه', 'ဟ', 'ှ', 'ჰ', 'h'], + 'i' => ['í', 'ì', 'ỉ', 'ĩ', 'ị', 'î', 'ï', 'ī', 'ĭ', 'į', + 'ı', 'ι', 'ί', 'ϊ', 'ΐ', 'ἰ', 'ἱ', 'ἲ', 'ἳ', 'ἴ', + 'ἵ', 'ἶ', 'ἷ', 'ὶ', 'ί', 'ῐ', 'ῑ', 'ῒ', 'ΐ', 'ῖ', + 'ῗ', 'і', 'ї', 'и', 'ဣ', 'ိ', 'ီ', 'ည်', 'ǐ', 'ი', + 'इ', 'ی', 'i'], + 'j' => ['ĵ', 'ј', 'Ј', 'ჯ', 'ج', 'j'], + 'k' => ['ķ', 'ĸ', 'к', 'κ', 'Ķ', 'ق', 'ك', 'က', 'კ', 'ქ', + 'ک', 'k'], + 'l' => ['ł', 'ľ', 'ĺ', 'ļ', 'ŀ', 'л', 'λ', 'ل', 'လ', 'ლ', + 'l'], + 'm' => ['м', 'μ', 'م', 'မ', 'მ', 'm'], + 'n' => ['ñ', 'ń', 'ň', 'ņ', 'ʼn', 'ŋ', 'ν', 'н', 'ن', 'န', + 'ნ', 'n'], + 'o' => ['ó', 'ò', 'ỏ', 'õ', 'ọ', 'ô', 'ố', 'ồ', 'ổ', 'ỗ', + 'ộ', 'ơ', 'ớ', 'ờ', 'ở', 'ỡ', 'ợ', 'ø', 'ō', 'ő', + 'ŏ', 'ο', 'ὀ', 'ὁ', 'ὂ', 'ὃ', 'ὄ', 'ὅ', 'ὸ', 'ό', + 'о', 'و', 'θ', 'ို', 'ǒ', 'ǿ', 'º', 'ო', 'ओ', 'o', + 'ö'], + 'p' => ['п', 'π', 'ပ', 'პ', 'پ', 'p'], + 'q' => ['ყ', 'q'], + 'r' => ['ŕ', 'ř', 'ŗ', 'р', 'ρ', 'ر', 'რ', 'r'], + 's' => ['ś', 'š', 'ş', 'с', 'σ', 'ș', 'ς', 'س', 'ص', 'စ', + 'ſ', 'ს', 's'], + 't' => ['ť', 'ţ', 'т', 'τ', 'ț', 'ت', 'ط', 'ဋ', 'တ', 'ŧ', + 'თ', 'ტ', 't'], + 'u' => ['ú', 'ù', 'ủ', 'ũ', 'ụ', 'ư', 'ứ', 'ừ', 'ử', 'ữ', + 'ự', 'û', 'ū', 'ů', 'ű', 'ŭ', 'ų', 'µ', 'у', 'ဉ', + 'ု', 'ူ', 'ǔ', 'ǖ', 'ǘ', 'ǚ', 'ǜ', 'უ', 'उ', 'u', + 'ў', 'ü'], + 'v' => ['в', 'ვ', 'ϐ', 'v'], + 'w' => ['ŵ', 'ω', 'ώ', 'ဝ', 'ွ', 'w'], + 'x' => ['χ', 'ξ', 'x'], + 'y' => ['ý', 'ỳ', 'ỷ', 'ỹ', 'ỵ', 'ÿ', 'ŷ', 'й', 'ы', 'υ', + 'ϋ', 'ύ', 'ΰ', 'ي', 'ယ', 'y'], + 'z' => ['ź', 'ž', 'ż', 'з', 'ζ', 'ز', 'ဇ', 'ზ', 'z'], + 'aa' => ['ع', 'आ', 'آ'], + 'ae' => ['æ', 'ǽ'], + 'ai' => ['ऐ'], + 'ch' => ['ч', 'ჩ', 'ჭ', 'چ'], + 'dj' => ['ђ', 'đ'], + 'dz' => ['џ', 'ძ'], + 'ei' => ['ऍ'], + 'gh' => ['غ', 'ღ'], + 'ii' => ['ई'], + 'ij' => ['ij'], + 'kh' => ['х', 'خ', 'ხ'], + 'lj' => ['љ'], + 'nj' => ['њ'], + 'oe' => ['œ', 'ؤ'], + 'oi' => ['ऑ'], + 'oii' => ['ऒ'], + 'ps' => ['ψ'], + 'sh' => ['ш', 'შ', 'ش'], + 'shch' => ['щ'], + 'ss' => ['ß'], + 'sx' => ['ŝ'], + 'th' => ['þ', 'ϑ', 'ث', 'ذ', 'ظ'], + 'ts' => ['ц', 'ც', 'წ'], + 'uu' => ['ऊ'], + 'ya' => ['я'], + 'yu' => ['ю'], + 'zh' => ['ж', 'ჟ', 'ژ'], + '(c)' => ['©'], + 'A' => ['Á', 'À', 'Ả', 'Ã', 'Ạ', 'Ă', 'Ắ', 'Ằ', 'Ẳ', 'Ẵ', + 'Ặ', 'Â', 'Ấ', 'Ầ', 'Ẩ', 'Ẫ', 'Ậ', 'Å', 'Ā', 'Ą', + 'Α', 'Ά', 'Ἀ', 'Ἁ', 'Ἂ', 'Ἃ', 'Ἄ', 'Ἅ', 'Ἆ', 'Ἇ', + 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'Ᾰ', 'Ᾱ', + 'Ὰ', 'Ά', 'ᾼ', 'А', 'Ǻ', 'Ǎ', 'A', 'Ä'], + 'B' => ['Б', 'Β', 'ब', 'B'], + 'C' => ['Ç','Ć', 'Č', 'Ĉ', 'Ċ', 'C'], + 'D' => ['Ď', 'Ð', 'Đ', 'Ɖ', 'Ɗ', 'Ƌ', 'ᴅ', 'ᴆ', 'Д', 'Δ', + 'D'], + 'E' => ['É', 'È', 'Ẻ', 'Ẽ', 'Ẹ', 'Ê', 'Ế', 'Ề', 'Ể', 'Ễ', + 'Ệ', 'Ë', 'Ē', 'Ę', 'Ě', 'Ĕ', 'Ė', 'Ε', 'Έ', 'Ἐ', + 'Ἑ', 'Ἒ', 'Ἓ', 'Ἔ', 'Ἕ', 'Έ', 'Ὲ', 'Е', 'Ё', 'Э', + 'Є', 'Ə', 'E'], + 'F' => ['Ф', 'Φ', 'F'], + 'G' => ['Ğ', 'Ġ', 'Ģ', 'Г', 'Ґ', 'Γ', 'G'], + 'H' => ['Η', 'Ή', 'Ħ', 'H'], + 'I' => ['Í', 'Ì', 'Ỉ', 'Ĩ', 'Ị', 'Î', 'Ï', 'Ī', 'Ĭ', 'Į', + 'İ', 'Ι', 'Ί', 'Ϊ', 'Ἰ', 'Ἱ', 'Ἳ', 'Ἴ', 'Ἵ', 'Ἶ', + 'Ἷ', 'Ῐ', 'Ῑ', 'Ὶ', 'Ί', 'И', 'І', 'Ї', 'Ǐ', 'ϒ', + 'I'], + 'J' => ['J'], + 'K' => ['К', 'Κ', 'K'], + 'L' => ['Ĺ', 'Ł', 'Л', 'Λ', 'Ļ', 'Ľ', 'Ŀ', 'ल', 'L'], + 'M' => ['М', 'Μ', 'M'], + 'N' => ['Ń', 'Ñ', 'Ň', 'Ņ', 'Ŋ', 'Н', 'Ν', 'N'], + 'O' => ['Ó', 'Ò', 'Ỏ', 'Õ', 'Ọ', 'Ô', 'Ố', 'Ồ', 'Ổ', 'Ỗ', + 'Ộ', 'Ơ', 'Ớ', 'Ờ', 'Ở', 'Ỡ', 'Ợ', 'Ø', 'Ō', 'Ő', + 'Ŏ', 'Ο', 'Ό', 'Ὀ', 'Ὁ', 'Ὂ', 'Ὃ', 'Ὄ', 'Ὅ', 'Ὸ', + 'Ό', 'О', 'Θ', 'Ө', 'Ǒ', 'Ǿ', 'O', 'Ö'], + 'P' => ['П', 'Π', 'P'], + 'Q' => ['Q'], + 'R' => ['Ř', 'Ŕ', 'Р', 'Ρ', 'Ŗ', 'R'], + 'S' => ['Ş', 'Ŝ', 'Ș', 'Š', 'Ś', 'С', 'Σ', 'S'], + 'T' => ['Ť', 'Ţ', 'Ŧ', 'Ț', 'Т', 'Τ', 'T'], + 'U' => ['Ú', 'Ù', 'Ủ', 'Ũ', 'Ụ', 'Ư', 'Ứ', 'Ừ', 'Ử', 'Ữ', + 'Ự', 'Û', 'Ū', 'Ů', 'Ű', 'Ŭ', 'Ų', 'У', 'Ǔ', 'Ǖ', + 'Ǘ', 'Ǚ', 'Ǜ', 'U', 'Ў', 'Ü'], + 'V' => ['В', 'V'], + 'W' => ['Ω', 'Ώ', 'Ŵ', 'W'], + 'X' => ['Χ', 'Ξ', 'X'], + 'Y' => ['Ý', 'Ỳ', 'Ỷ', 'Ỹ', 'Ỵ', 'Ÿ', 'Ῠ', 'Ῡ', 'Ὺ', 'Ύ', + 'Ы', 'Й', 'Υ', 'Ϋ', 'Ŷ', 'Y'], + 'Z' => ['Ź', 'Ž', 'Ż', 'З', 'Ζ', 'Z'], + 'AE' => ['Æ', 'Ǽ'], + 'Ch' => ['Ч'], + 'Dj' => ['Ђ'], + 'Dz' => ['Џ'], + 'Gx' => ['Ĝ'], + 'Hx' => ['Ĥ'], + 'Ij' => ['IJ'], + 'Jx' => ['Ĵ'], + 'Kh' => ['Х'], + 'Lj' => ['Љ'], + 'Nj' => ['Њ'], + 'Oe' => ['Œ'], + 'Ps' => ['Ψ'], + 'Sh' => ['Ш'], + 'Shch' => ['Щ'], + 'Ss' => ['ẞ'], + 'Th' => ['Þ'], + 'Ts' => ['Ц'], + 'Ya' => ['Я'], + 'Yu' => ['Ю'], + 'Zh' => ['Ж'], + ' ' => ["\xC2\xA0", "\xE2\x80\x80", "\xE2\x80\x81", + "\xE2\x80\x82", "\xE2\x80\x83", "\xE2\x80\x84", + "\xE2\x80\x85", "\xE2\x80\x86", "\xE2\x80\x87", + "\xE2\x80\x88", "\xE2\x80\x89", "\xE2\x80\x8A", + "\xE2\x80\xAF", "\xE2\x81\x9F", "\xE3\x80\x80", + "\xEF\xBE\xA0"], + ]; + } + + public function toSpaces($tabLength = 4) + { + $spaces = str_repeat(' ', $tabLength); + $str = str_replace("\t", $spaces, $this->str); + return static::create($str, $this->encoding); + } + + public function underscored() + { + return $this->delimit('_'); + } + + public function toUpperCase() + { + $str = \mb_strtoupper($this->str, $this->encoding); + return static::create($str, $this->encoding); + } + + public function truncate($length, $substring = '') + { + $stringy = static::create($this->str, $this->encoding); + if ($length >= $stringy->length()) { + return $stringy; + } + // Need to further trim the string so we can append the substring + $substringLength = \mb_strlen($substring, $stringy->encoding); + $length = $length - $substringLength; + $truncated = \mb_substr($stringy->str, 0, $length, $stringy->encoding); + $stringy->str = $truncated . $substring; + return $stringy; + } + + public function toTitleCase() + { + $str = \mb_convert_case($this->str, \MB_CASE_TITLE, $this->encoding); + return static::create($str, $this->encoding); + } + + public function toTabs($tabLength = 4) + { + $spaces = str_repeat(' ', $tabLength); + $str = str_replace($spaces, "\t", $this->str); + return static::create($str, $this->encoding); + } + + public function toLowerCase() + { + $str = \mb_strtolower($this->str, $this->encoding); + + return static::create($str, $this->encoding); + } + + public function toBoolean() + { + $key = $this->toLowerCase()->str; + $map = [ + 'true' => true, + '1' => true, + 'on' => true, + 'yes' => true, + 'false' => false, + '0' => false, + 'off' => false, + 'no' => false + ]; + + if (array_key_exists($key, $map)) { + return $map[$key]; + } elseif (is_numeric($this->str)) { + return (intval($this->str) > 0); + } + + return (bool) $this->regexReplace('[[:space:]]', '')->str; + } + + public function titleize($ignore = null) + { + $stringy = static::create($this->trim(), $this->encoding); + $encoding = $this->encoding; + + $stringy->str = preg_replace_callback( + '/([\S]+)/u', + function ($match) use ($encoding, $ignore) { + if ($ignore && in_array($match[0], $ignore)) { + return $match[0]; + } + + $stringy = new Stringy($match[0], $encoding); + + return (string) $stringy->toLowerCase()->upperCaseFirst(); + }, + $stringy->str + ); + + return $stringy; + } + + public function reverse() + { + $strLength = $this->length(); + $reversed = ''; + + // Loop from last index of string to first + for ($i = $strLength - 1; $i >= 0; $i--) { + $reversed .= \mb_substr($this->str, $i, 1, $this->encoding); + } + + return static::create($reversed, $this->encoding); + } + + public function safeTruncate($length, $substring = '') + { + $stringy = static::create($this->str, $this->encoding); + if ($length >= $stringy->length()) { + return $stringy; + } + + // Need to further trim the string so we can append the substring + $encoding = $stringy->encoding; + $substringLength = \mb_strlen($substring, $encoding); + $length = $length - $substringLength; + + $truncated = \mb_substr($stringy->str, 0, $length, $encoding); + + // If the last word was truncated + if (mb_strpos($stringy->str, ' ', $length - 1, $encoding) != $length) { + // Find pos of the last occurrence of a space, get up to that + $lastPos = \mb_strrpos($truncated, ' ', 0, $encoding); + if ($lastPos !== false) { + $truncated = \mb_substr($truncated, 0, $lastPos, $encoding); + } + } + + $stringy->str = $truncated . $substring; + + return $stringy; + } + + public function shuffle() + { + $indexes = range(0, $this->length() - 1); + shuffle($indexes); + + $shuffledStr = ''; + foreach ($indexes as $i) { + $shuffledStr .= \mb_substr($this->str, $i, 1, $this->encoding); + } + + return static::create($shuffledStr, $this->encoding); + } +} diff --git a/src/Minbaby/Test.php b/src/Minbaby/Php/Test.php similarity index 85% rename from src/Minbaby/Test.php rename to src/Minbaby/Php/Test.php index 8b11298..711332a 100644 --- a/src/Minbaby/Test.php +++ b/src/Minbaby/Php/Test.php @@ -1,6 +1,6 @@ = 0) + { + RETURN_BOOL(length > offset_int); + } + + RETURN_BOOL(length >= abs(offset_int)); +} +ZEND_BEGIN_ARG_INFO(arginfo_offsetExists, 1) +ZEND_ARG_INFO(0, offset) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, offsetGet) +{ + zval *offset; + int offset_int = 0, length = 0; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(offset) + ZEND_PARSE_PARAMETERS_END(); + + convert_to_long(offset); + + offset_int = Z_LVAL_P(offset); + + zval func; + ZVAL_STRING(&func, "length"); + call_user_function(NULL, getThis(), &func, return_value, 0, NULL); + length = Z_LVAL_P(return_value); + + if ((offset_int >= 0 && length <= offset_int) || length < abs(offset_int)) + { + zend_throw_exception(spl_ce_OutOfBoundsException, "No character exists at the index", 0); + return; + } + + zval args[4], substrLen, rv; + ZVAL_LONG(&substrLen, 1); + ZVAL_STRING(&func, "mb_substr"); + zval *string = zend_read_property(stringy_ce, getThis(), "str", strlen("str"), 1, &rv); + zval *encoding = zend_read_property(stringy_ce, getThis(), "encoding", strlen("encoding"), 1, &rv); + args[0] = *string; + args[1] = *offset; + args[2] = substrLen; + args[3] = *encoding; + call_user_function(NULL, NULL, &func, return_value, 4, args); +} +ZEND_BEGIN_ARG_INFO(arginfo_offsetGet, 1) +ZEND_ARG_INFO(0, offset) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, offsetSet) +{ + zend_throw_exception_ex(zend_ce_exception, 0, "%s", "Stringy object is immutable, cannot modify char"); +} +ZEND_BEGIN_ARG_INFO(arginfo_offsetSet, 2) +ZEND_ARG_INFO(0, offset) +ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, offsetUnset) +{ + zend_throw_exception_ex(zend_ce_exception, 0, "%s", "Stringy object is immutable, cannot unset char"); +} +ZEND_BEGIN_ARG_INFO(arginfo_offsetUnset, 1) +ZEND_ARG_INFO(0, offset) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, collapseWhiteSpace) +{ + zval retval, func; + zval pattern, replacement, options, chars; + + ZVAL_STRING(&pattern, "[[:space:]]+"); + ZVAL_STRING(&replacement, " "); + + zval args[] = { + pattern, + replacement, + }; + + ZVAL_STRING(&func, "regexReplace"); + call_user_function(NULL, getThis(), &func, return_value, 2, args); + + ZVAL_STRING(&func, "trim"); + call_user_function(NULL, return_value, &func, return_value, 0, NULL); +} + +PHP_METHOD(Stringy, regexReplace) +{ + zval *pattern, *replacement, *options = NULL, rv; + zval func; + + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_ZVAL(pattern) + Z_PARAM_ZVAL(replacement) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(options) + ZEND_PARSE_PARAMETERS_END(); + + if (options == NULL) { + options = emalloc(sizeof(zval)); + ZVAL_STRING(options, "msr"); + } + + zval *encoding = zend_read_property(stringy_ce, getThis(), "encoding", strlen("encoding"), 0, &rv); + zval *str = zend_read_property(stringy_ce, getThis(), "str", strlen("str"), 1, &rv); + + zval regexEncoding; + ZVAL_STRING(&func, "regexEncoding"); + call_user_function(NULL, getThis(), &func, ®exEncoding, 0, NULL); + + zval args[1]; + ZVAL_STRING(&func, "regexEncoding"); + args[0] = *encoding; + call_user_function(NULL, getThis(), &func, return_value, 1, args); + + zval retStr; + ZVAL_STRING(&func, "eregReplace"); + + zval args_eregReplace[] = { + *pattern, + *replacement, + *str, + *options, + }; + call_user_function(NULL, getThis(), &func, &retStr, 4, args_eregReplace); + + ZVAL_STRING(&func, "regexEncoding"); + args[0] = regexEncoding; + call_user_function(NULL, getThis(), &func, return_value, 1, args); + + zval this; + object_init_ex(&this, stringy_ce); + zend_call_method(&this, stringy_ce, NULL, ZEND_STRL("__construct"), return_value, 2, &retStr, encoding); + + RETURN_ZVAL(&this, 0, 1); +} +ZEND_BEGIN_ARG_INFO(arginfo_regexReplace, 3) +ZEND_ARG_INFO(0, pattern) +ZEND_ARG_INFO(0, replacement) +ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, trim) +{ + zval *chars = NULL; + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(chars) + ZEND_PARSE_PARAMETERS_END(); + + if (chars != NULL && Z_TYPE_P(chars) != IS_NULL) + { + convert_to_string(chars); + if (Z_STRLEN_P(chars) == 0) { + chars = emalloc(sizeof(zval)); + ZVAL_STRING(chars, "[:space:]"); + } else { + zval func, delimiter; + ZVAL_STRING(&func, "preg_quote"); + zval args[] = { + *chars, + }; + call_user_function(NULL, NULL, &func, return_value, 1, args); + ZVAL_STRING(chars, Z_STRVAL_P(return_value)); + } + } + else + { + chars = emalloc(sizeof(zval)); + ZVAL_STRING(chars, "[:space:]"); + + } + + zval func_regexReplace, pattern, replacement, options; + ZVAL_STRING(&func_regexReplace, "regexReplace"); + zend_string *p = strpprintf(0, "^[%s]+|[%s]+$", Z_STRVAL_P(chars), Z_STRVAL_P(chars)); + ZVAL_STR(&pattern, p); + ZVAL_STRING(&replacement, ""); + zval args[] = { + pattern, + replacement, + }; + call_user_function(NULL, getThis(), &func_regexReplace, return_value, 2, args); +} +ZEND_BEGIN_ARG_INFO(arginfo_trim, 1) +ZEND_ARG_INFO(0, chars) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, regexEncoding) +{ + zval *encoding; + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(encoding) + ZEND_PARSE_PARAMETERS_END(); + + zval func, args[1]; + ZVAL_STRING(&func, "mb_regex_encoding"); + args[0] = *encoding; + call_user_function(NULL, NULL, &func, return_value, 0, NULL); +} +ZEND_BEGIN_ARG_INFO(arginfo_regexEncoding, 1) +ZEND_ARG_INFO(0, encoding) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, eregReplace) +{ + zval *pattern, *replace, *string, *option = NULL; + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_ZVAL(pattern) + Z_PARAM_ZVAL(replace) + Z_PARAM_ZVAL(string) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(option) + ZEND_PARSE_PARAMETERS_END(); + + zval func; + ZVAL_STRING(&func, "mb_ereg_replace"); + + if (option == NULL) { + option = emalloc(sizeof(zval)); + ZVAL_EMPTY_STRING(option); + } + + convert_to_string(replace); + convert_to_string(pattern); + convert_to_string(string); + convert_to_string(option); + + zval args[] = { + *pattern, + *replace, + *string, + *option, + }; + call_user_function(NULL, NULL, &func, return_value, 4, args); +} +ZEND_BEGIN_ARG_INFO(arginfo_eregReplace, 4) +ZEND_ARG_INFO(0, pattern) +ZEND_ARG_INFO(0, replacement) +ZEND_ARG_INFO(0, string) +ZEND_ARG_INFO(0, option) +ZEND_END_ARG_INFO(); + +static void swap_case_handler(INTERNAL_FUNCTION_PARAMETERS) +{ + zval *arr; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(arr) + ZEND_PARSE_PARAMETERS_END(); + + zval *first = zend_hash_index_find(Z_ARRVAL_P(arr), 0); + + if (first == NULL) { + RETURN_EMPTY_STRING(); + } + + zval func, ret; + ZVAL_STRING(&func, "mb_strtoupper"); + zval args[] = { + *first, + }; + call_user_function(NULL, NULL, &func, &ret, 1, args); + + if (zend_string_equals(Z_STR_P(&ret), Z_STR_P(first)) == 1) { + ZVAL_STRING(&func, "mb_strtolower"); + zval args[] = { + *first, + }; + call_user_function(NULL, NULL, &func, &ret, 1, args); + } + + RETURN_ZVAL(&ret, 0, 1); +} + +PHP_METHOD(Stringy, swapCase) +{ + zval rv, func, pattern={}, subject, limit_str, count, ret; + + ZVAL_STRING(&pattern, "/[\\S]/u"); + + subject = *zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + + zend_function zendFunction; + + zend_internal_arg_info zai[] = { + ZEND_ARG_ARRAY_INFO(0, "matches", 0) + }; + + zend_string *f = zend_string_init(ZEND_STRL("callback"), 0); + + // zendFunction.type = ZEND_INTERNAL_FUNCTION; + // zendFunction.common.num_args = 1; + // zendFunction.common.required_num_args = 1; + // zendFunction.common.arg_info = zai; + // zendFunction.common.prototype = NULL; + // zendFunction.common.scope = NULL; + // zendFunction.common.fn_flags = ZEND_ACC_CLOSURE; + zendFunction.internal_function.function_name = f; + zendFunction.internal_function.handler = swap_case_handler; + zendFunction.internal_function.type = ZEND_INTERNAL_FUNCTION; + zendFunction.internal_function.fn_flags = ZEND_ACC_CLOSURE; + zendFunction.internal_function.arg_info = zai; + zendFunction.internal_function.required_num_args =1; + zendFunction.internal_function.num_args = 1; + + zval callback; + zend_create_closure(&callback, &zendFunction, NULL, NULL, NULL); + + ZVAL_STRING(&func, "preg_replace_callback"); + zval args[] ={ + pattern, + callback, + subject, + }; + call_user_function(NULL, NULL, &func, &ret, 3, args); + + convert_to_string(&ret); + zend_update_property_string(stringy_ce, getThis(), ZEND_STRL("str"), Z_STRVAL(ret)); + + RETURN_ZVAL(getThis(), 1, 0); +} + +PHP_METHOD(Stringy, upperCaseFirst) +{ + zval instance, encoding, str, func, ret, start, len, first, rest, rv; + + ZVAL_STRING(&func, "length"); + call_user_function(NULL, getThis(), &func, return_value, 0, NULL); + size_t length = Z_LVAL_P(return_value); + + str = *zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + encoding = *zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + ZVAL_LONG(&start, 0); + ZVAL_LONG(&len, 1); + zval args[] = { + str, + start, + len, + encoding, + }; + ZVAL_STRING(&func, "mb_substr"); + call_user_function(NULL, NULL, &func, &first, 4, args); + + ZVAL_LONG(&start, 1); + ZVAL_LONG(&len, length-1); + zval args_rest[] = { + str, + start, + len, + encoding, + }; + ZVAL_STRING(&func, "mb_substr"); + call_user_function(NULL, NULL, &func, &rest, 4, args_rest); + + zval args_upper[] = { + first, + encoding, + }; + ZVAL_STRING(&func, "mb_strtoupper"); + call_user_function(NULL, NULL, &func, &first, 2, args_upper); + + concat_function(&ret, &first, &rest); + + zend_string *str_string = Z_STR(ret); + zend_update_property_str(stringy_ce, getThis(), ZEND_STRL("str"), str_string); + + RETURN_ZVAL(getThis(), 1, 0); +} + +PHP_METHOD(Stringy, lowerCaseFirst) +{ + zval instance, encoding, str, func, ret, start, len, first, rest, rv; + + ZVAL_STRING(&func, "length"); + call_user_function(NULL, getThis(), &func, return_value, 0, NULL); + size_t length = Z_LVAL_P(return_value); + + str = *zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + encoding = *zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + ZVAL_LONG(&start, 0); + ZVAL_LONG(&len, 1); + zval args[] = { + str, + start, + len, + encoding, + }; + ZVAL_STRING(&func, "mb_substr"); + call_user_function(NULL, NULL, &func, &first, 4, args); + + ZVAL_LONG(&start, 1); + ZVAL_LONG(&len, length-1); + zval args_rest[] = { + str, + start, + len, + encoding, + }; + ZVAL_STRING(&func, "mb_substr"); + call_user_function(NULL, NULL, &func, &rest, 4, args_rest); + + zval args_upper[] = { + first, + encoding, + }; + ZVAL_STRING(&func, "mb_strtolower"); + call_user_function(NULL, NULL, &func, &first, 2, args_upper); + + concat_function(&ret, &first, &rest); + + zval this; + object_init_ex(&this, stringy_ce); + zend_call_method(&this, stringy_ce, NULL, ZEND_STRL("__construct"), return_value, 2, &ret, &encoding); + + RETURN_ZVAL(&this, 1, 0); +} + +PHP_METHOD(Stringy, append) +{ + zval *str_param; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(str_param) + ZEND_PARSE_PARAMETERS_END(); + + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 1, &rv); + + concat_function(return_value, str, str_param); + + zend_update_property_string(stringy_ce, getThis(), ZEND_STRL("str"), Z_STRVAL_P(return_value)); + + RETURN_ZVAL(getThis(), 1, 0); +} +ZEND_BEGIN_ARG_INFO(arginfo_append, 1) + ZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, prepend) +{ + zval *str_param; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(str_param) + ZEND_PARSE_PARAMETERS_END(); + + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 1, &rv); + + concat_function(return_value, str_param, str); + + zend_update_property_string(stringy_ce, getThis(), ZEND_STRL("str"), Z_STRVAL_P(return_value)); + + RETURN_ZVAL(getThis(), 1, 0); +} +ZEND_BEGIN_ARG_INFO(arginfo_prepend, 1) + ZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, lines) +{ + zval array, pattern, *str, rv; + + ZVAL_STRING(&pattern, "[\r\n]{1,2}"); + str =zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 1, &rv); + + array_init(&array); + + zval func, args[] = { + pattern, + *str, + }; + ZVAL_STRING(&func, "split"); + call_user_function(NULL, getThis(), &func, &array, 2, args); + + zval ret_arr; + array_init(&ret_arr); + + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + zval *tmp; + + ZEND_HASH_FOREACH_VAL(Z_ARRVAL(array), tmp){ + zval instance; + object_init_ex(&instance, stringy_ce); + convert_to_string(tmp); + zend_call_method(&instance, stringy_ce, NULL, ZEND_STRL("__construct"), return_value, 2, tmp, encoding); + add_next_index_zval(&ret_arr, &instance); + }ZEND_HASH_FOREACH_END(); + + RETURN_ZVAL(&ret_arr, 0, 1); +} + +PHP_METHOD(Stringy, split) +{ + zend_string *empty = zend_string_init(ZEND_STRL(""), 0); + zend_string *pattern; + zval *limit_zval; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(pattern) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(limit_zval) + ZEND_PARSE_PARAMETERS_END(); + + zval pattern_zval, str_zval, rv; + + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + str_zval = *zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + + zval arr; + array_init(&arr); + + if (Z_TYPE_P(limit_zval) == IS_LONG && Z_LVAL_P(limit_zval) == 0) { + RETURN_ZVAL(&arr, 0, 1); + } + + if (zend_string_equals(pattern, empty)) { + zval instance; + zval tmp; + ZVAL_EMPTY_STRING(&tmp); + + object_init_ex(&instance, stringy_ce); + zend_call_method(&instance, stringy_ce, NULL, ZEND_STRL("__construct"), return_value, 2, &str_zval, encoding); + + add_index_zval(&arr, 0, &instance); + RETURN_ZVAL(&arr, 1, 0); + } + + convert_to_long(limit_zval); + zend_long limit_long = Z_LVAL_P(limit_zval); + + zend_long limit = (limit_long > 0) ? limit_long += 1 : -1; + + ZVAL_STR(&pattern_zval, pattern); + + ZVAL_LONG(limit_zval, limit); + zval func, args[] = { + pattern_zval, + str_zval, + *limit_zval, + }; + ZVAL_STRING(&func, "mb_split"); + call_user_function(NULL, NULL, &func, &arr, 3, args); + + zend_long array_len = zend_array_count(Z_ARRVAL(arr)); + + zval *tmp; + zend_long index; + zval ret_arr; + array_init(&ret_arr); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL(arr), tmp){ + zval instance; + object_init_ex(&instance, stringy_ce); + zend_call_method(&instance, stringy_ce, NULL, ZEND_STRL("__construct"), return_value, 2, tmp, encoding); + add_next_index_zval(&ret_arr, &instance); + }ZEND_HASH_FOREACH_END(); + + + RETURN_ZVAL(&ret_arr, 0, 1); +} +ZEND_BEGIN_ARG_INFO(arginfo_split, 2) + ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0) + ZEND_ARG_INFO(0, limit_str) +ZEND_END_ARG_INFO(); + +static void preg_replace_callback_handler(INTERNAL_FUNCTION_PARAMETERS) +{ + zval *arr = NULL; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(arr) + ZEND_PARSE_PARAMETERS_END(); + + if (arr == NULL || zend_array_count(Z_ARRVAL_P(arr)) <= 1) { + RETURN_EMPTY_STRING(); + } + + zval *first = zend_hash_index_find(Z_ARRVAL_P(arr), 1); + // php_var_dump(arr, 1); + // php_var_dump(first, 1); + //TODO: 不知道这里如何模拟 php 里边的use, 这里先写死了 + zval encoding; + ZVAL_STRING(&encoding, "UTF-8"); + zval func, args[] = { + *first, + encoding, + }; + ZVAL_STRING(&func, "mb_strtoupper"); + call_user_function(NULL, NULL, &func, return_value, 2, args); +} + +static void preg_replace_callback_2_handler(INTERNAL_FUNCTION_PARAMETERS) +{ + zval *arr = NULL; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(arr) + ZEND_PARSE_PARAMETERS_END(); + + if (arr == NULL || zend_array_count(Z_ARRVAL_P(arr)) < 1) { + RETURN_EMPTY_STRING(); + } + + zval *first = zend_hash_index_find(Z_ARRVAL_P(arr), 0); + + //TODO: 不知道这里如何模拟 php 里边的use, 这里先写死了 + zval encoding; + ZVAL_STRING(&encoding, "UTF-8"); + zval func, args[] = { + *first, + encoding, + }; + ZVAL_STRING(&func, "mb_strtoupper"); + call_user_function(NULL, NULL, &func, return_value, 2, args); +} + +PHP_METHOD(Stringy, camelize) +{ + zval rv, func; + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 1, &rv); + + zval instance; + + ZVAL_STRING(&func, "trim"); + call_user_function(NULL, getThis(), &func, return_value, 0, NULL); + + ZVAL_STRING(&func, "lowerCaseFirst"); + call_user_function(NULL, return_value, &func, &instance, 0, NULL); + + zval *str_zval = zend_read_property(stringy_ce, &instance, ZEND_STRL("str"), 1, &rv); + + zval pattern, replacement; + ZVAL_STRING(&pattern, "/^[-_]+/"); + ZVAL_EMPTY_STRING(&replacement); + zval args[] = { + pattern, + replacement, + *str_zval, + }; + ZVAL_STRING(&func, "preg_replace"); + call_user_function(NULL, NULL, &func, return_value, 3, args); + zend_update_property(stringy_ce, &instance, ZEND_STRL("str"), return_value); + + zend_function zendFunction; + + zend_internal_arg_info zai[] = { + ZEND_ARG_ARRAY_INFO(0, "matches", 0) + }; +#pragma region first + zend_string *f = zend_string_init(ZEND_STRL("callback"), 0); + + // zendFunction.type = ZEND_INTERNAL_FUNCTION; + // zendFunction.common.num_args = 1; + // zendFunction.common.required_num_args = 1; + // zendFunction.common.arg_info = zai; + // zendFunction.common.prototype = NULL; + // zendFunction.common.scope = NULL; + // zendFunction.common.fn_flags = ZEND_ACC_CLOSURE; + zendFunction.internal_function.function_name = f; + zendFunction.internal_function.handler = preg_replace_callback_handler; + zendFunction.internal_function.type = ZEND_INTERNAL_FUNCTION; + zendFunction.internal_function.fn_flags = ZEND_ACC_CLOSURE; + zendFunction.internal_function.arg_info = zai; + zendFunction.internal_function.required_num_args =1; + zendFunction.internal_function.num_args = 1; + + zval callback; + zend_create_closure(&callback, &zendFunction, NULL, NULL, NULL); + + str_zval = zend_read_property(stringy_ce, &instance, ZEND_STRL("str"), 1, &rv); + ZVAL_STRING(&pattern, "/[-_\\s]+(.)?/u"); + ZVAL_EMPTY_STRING(&replacement); + zval args_callback[] = { + pattern, + callback, + *str_zval, + }; + ZVAL_STRING(&func, "preg_replace_callback"); + call_user_function(NULL, NULL, &func, return_value, 3, args_callback); +#pragma endregion + + zend_update_property(stringy_ce, &instance, ZEND_STRL("str"), return_value); + +#pragma region second + + zendFunction.internal_function.handler = preg_replace_callback_2_handler; + + zend_create_closure(&callback, &zendFunction, NULL, NULL, NULL); + + str_zval = zend_read_property(stringy_ce, &instance, ZEND_STRL("str"), 1, &rv); + ZVAL_STRING(&pattern, "/[\\d]+(.)?/u"); + ZVAL_EMPTY_STRING(&replacement); + args_callback[0] = pattern; + args_callback[1] = callback; + args_callback[2] = *str_zval; + + ZVAL_STRING(&func, "preg_replace_callback"); + call_user_function(NULL, NULL, &func, return_value, 3, args_callback); +#pragma endregion + + zend_update_property(stringy_ce, &instance, ZEND_STRL("str"), return_value); + + RETURN_ZVAL(&instance, 1, 0); +} + +PHP_METHOD(Stringy, trimLeft) +{ + zval *chars = NULL; + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(chars) + ZEND_PARSE_PARAMETERS_END(); + + if (chars != NULL && Z_TYPE_P(chars) != IS_NULL) + { + convert_to_string(chars); + if (Z_STRLEN_P(chars) == 0) { + chars = emalloc(sizeof(zval)); + ZVAL_STRING(chars, "[:space:]"); + } else { + zval func, delimiter; + ZVAL_STRING(&func, "preg_quote"); + zval args[] = { + *chars, + }; + call_user_function(NULL, NULL, &func, return_value, 1, args); + ZVAL_STRING(chars, Z_STRVAL_P(return_value)); + } + } + else + { + chars = emalloc(sizeof(zval)); + ZVAL_STRING(chars, "[:space:]"); + + } + + zval func_regexReplace, pattern, replacement, options; + ZVAL_STRING(&func_regexReplace, "regexReplace"); + zend_string *p = strpprintf(0, "^[%s]+", Z_STRVAL_P(chars)); + ZVAL_STR(&pattern, p); + ZVAL_STRING(&replacement, ""); + zval args[] = { + pattern, + replacement, + }; + call_user_function(NULL, getThis(), &func_regexReplace, return_value, 2, args); +} +ZEND_BEGIN_ARG_INFO(arginfo_trimLeft, 1) +ZEND_ARG_INFO(0, chars) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, trimRight) +{ + zval *chars = NULL; + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(chars) + ZEND_PARSE_PARAMETERS_END(); + + if (chars != NULL && Z_TYPE_P(chars) != IS_NULL) + { + convert_to_string(chars); + if (Z_STRLEN_P(chars) == 0) { + chars = emalloc(sizeof(zval)); + ZVAL_STRING(chars, "[:space:]"); + } else { + zval func, delimiter; + ZVAL_STRING(&func, "preg_quote"); + zval args[] = { + *chars, + }; + call_user_function(NULL, NULL, &func, return_value, 1, args); + ZVAL_STRING(chars, Z_STRVAL_P(return_value)); + } + } + else + { + chars = emalloc(sizeof(zval)); + ZVAL_STRING(chars, "[:space:]"); + + } + + zval func_regexReplace, pattern, replacement, options; + ZVAL_STRING(&func_regexReplace, "regexReplace"); + zend_string *p = strpprintf(0, "[%s]+$", Z_STRVAL_P(chars)); + ZVAL_STR(&pattern, p); + ZVAL_STRING(&replacement, ""); + zval args[] = { + pattern, + replacement, + }; + call_user_function(NULL, getThis(), &func_regexReplace, return_value, 2, args); +} +ZEND_BEGIN_ARG_INFO(arginfo_trimRight, 1) +ZEND_ARG_INFO(0, chars) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, between) +{ + zval *start_zval, *end_zval, *offset_zval = NULL; + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_ZVAL(start_zval) + Z_PARAM_ZVAL(end_zval) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(offset_zval) + ZEND_PARSE_PARAMETERS_END(); + + convert_to_string(start_zval); + convert_to_string(start_zval); + convert_to_long(offset_zval); + + zval rv; + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval empty; + ZVAL_EMPTY_STRING(&empty); + + zval instance; + object_init_ex(&instance, stringy_ce); + zend_call_method(&instance, stringy_ce, NULL, ZEND_STRL("__construct"), return_value, 2, &empty, encoding); + + zval func, args[] = { + *start_zval, + *offset_zval, + }; + ZVAL_STRING(&func, "indexOf"); + call_user_function(NULL, getThis(), &func, return_value, 2, args); + if (Z_TYPE_P(return_value) == IS_FALSE) { + RETURN_ZVAL(&instance, 0, 1); + } + size_t start_index_l = Z_LVAL_P(return_value); + + zval args_strlen[] = { + *start_zval, + *encoding, + }; + ZVAL_STRING(&func, "mb_strlen"); + call_user_function(NULL, NULL, &func, return_value, 2, args_strlen); + + size_t substrIndex = start_index_l + Z_LVAL_P(return_value); + zval substrIndex_zval; + ZVAL_LONG(&substrIndex_zval, substrIndex); + + zval args_indexOf[] = { + *end_zval, + substrIndex_zval, + }; + ZVAL_STRING(&func, "indexOf"); + call_user_function(NULL, getThis(), &func, return_value, 2, args_indexOf); + if (Z_TYPE_P(return_value) == IS_FALSE) { + RETURN_ZVAL(&instance, 0, 1); + } + size_t x_l = Z_LVAL_P(return_value) - substrIndex; + zval x_zval; + ZVAL_LONG(&x_zval, x_l); + + zval args_substr[] = { + substrIndex_zval, + x_zval, + }; + ZVAL_STRING(&func, "substr"); + call_user_function(NULL, getThis(), &func, return_value, 2, args_substr); +} +ZEND_BEGIN_ARG_INFO(arginfo_between, 3) + ZEND_ARG_INFO(0, start) + ZEND_ARG_INFO(0, end) + ZEND_ARG_INFO(0, offset) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, contains) +{ + zval *needle, *caseSensitive = NULL; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(needle) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(caseSensitive) + ZEND_PARSE_PARAMETERS_END(); + + convert_to_boolean(caseSensitive); + + zval rv; + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + + zval z, func; + ZVAL_LONG(&z, 0); + + if (Z_TYPE_P(caseSensitive) == IS_TRUE) { + zval args[] = { + *str, + *needle, + z, + *encoding, + }; + ZVAL_STRING(&func, "mb_strpos"); + call_user_function(NULL, NULL, &func, return_value, 4, args); + ZVAL_BOOL(return_value, Z_TYPE_P(return_value) != IS_FALSE); + return; + } + + zval args_i[] = { + *str, + *needle, + z, + *encoding, + }; + ZVAL_STRING(&func, "mb_stripos"); + call_user_function(NULL, NULL, &func, return_value, 4, args_i); + ZVAL_BOOL(return_value, Z_TYPE_P(return_value) != IS_FALSE); +} +ZEND_BEGIN_ARG_INFO(arginfo_contains, 2) + ZEND_ARG_INFO(0, needle) + ZEND_ARG_INFO(0, caseSensitive) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, containsAll) +{ + zval *needles = NULL, *caseSensitive = NULL; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(needles) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(caseSensitive) + ZEND_PARSE_PARAMETERS_END(); + + zval rv; + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + + convert_to_array(needles); + + if (zend_array_count(Z_ARRVAL_P(needles)) == 0) { + RETURN_BOOL(0); + } + + zval *needle; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(needles), needle){ + zval func, args[] = { + *needle, + *caseSensitive + }; + ZVAL_STRING(&func, "contains"); + call_user_function(NULL, getThis(), &func, return_value, 2, args); + if (Z_TYPE_P(return_value) == IS_FALSE) { + RETURN_BOOL(0); + } + }ZEND_HASH_FOREACH_END(); + + RETURN_BOOL(1); +} +ZEND_BEGIN_ARG_INFO(arginfo_containsAll, 2) + ZEND_ARG_INFO(0, needle) + ZEND_ARG_INFO(0, caseSensitive) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, containsAny) +{ + zval *needles = NULL, *caseSensitive = NULL; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(needles) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(caseSensitive) + ZEND_PARSE_PARAMETERS_END(); + + zval rv; + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + + convert_to_array(needles); + + if (zend_array_count(Z_ARRVAL_P(needles)) == 0) { + RETURN_BOOL(0); + } + + zval *needle; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(needles), needle){ + zval func, args[] = { + *needle, + *caseSensitive + }; + ZVAL_STRING(&func, "contains"); + call_user_function(NULL, getThis(), &func, return_value, 2, args); + if (Z_TYPE_P(return_value) == IS_TRUE) { + RETURN_BOOL(1); + } + }ZEND_HASH_FOREACH_END(); + + RETURN_BOOL(0); +} +ZEND_BEGIN_ARG_INFO(arginfo_containsAny, 2) + ZEND_ARG_INFO(0, needle) + ZEND_ARG_INFO(0, caseSensitive) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, countSubstr) +{ + zval *substring = NULL, *caseSensitive = NULL; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(substring) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(caseSensitive) + ZEND_PARSE_PARAMETERS_END(); + + zval rv; + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + + if (Z_TYPE_P(caseSensitive) == IS_TRUE) { + zval func, args[] = { + *str, + *substring, + *encoding, + }; + ZVAL_STRING(&func, "mb_substr_count"); + call_user_function(NULL, NULL, &func, return_value, 3, args); + return; + } + + zval str_zval, substring_zval; + zval func, args[] = { + *str, + *encoding, + }; + ZVAL_STRING(&func, "mb_strtoupper"); + call_user_function(NULL, NULL, &func, &str_zval, 2, args); + + zval args_substring[] = { + *substring, + *encoding, + }; + ZVAL_STRING(&func, "mb_strtoupper"); + call_user_function(NULL, NULL, &func, &substring_zval, 2, args_substring); + + zval args_count[] = { + str_zval, + substring_zval, + *encoding, + }; + ZVAL_STRING(&func, "mb_substr_count"); + call_user_function(NULL, NULL, &func, return_value, 3, args_count); +} +ZEND_BEGIN_ARG_INFO(arginfo_countSubstr, 2) + ZEND_ARG_INFO(0, substring) + ZEND_ARG_INFO(0, caseSensitive) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, delimit) +{ + zval *delimiter = NULL; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(delimiter) + ZEND_PARSE_PARAMETERS_END(); + + zval rv, ret; + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval func; + ZVAL_STRING(&func, "trim"); + call_user_function(NULL, getThis(), &func, return_value, 0, NULL); + + zval pattern, replacement; + ZVAL_STRING(&pattern, "\\B([A-Z])"); + ZVAL_STRING(&replacement, "-\\1"); + zval args[] = { + pattern, + replacement, + *return_value + }; + ZVAL_STRING(&func, "eregReplace"); + call_user_function(NULL, getThis(), &func, &ret, 3, args); + + zval args_strtolower[] = { + ret, + *encoding, + }; + ZVAL_STRING(&func, "mb_strtolower"); + call_user_function(NULL, NULL, &func, return_value, 2, args_strtolower); + + ZVAL_STRING(&pattern, "[-_\\s]+"); + zval args_eregReplace[] = { + pattern, + *delimiter, + *return_value + }; + ZVAL_STRING(&func, "eregReplace"); + call_user_function(NULL, getThis(), &func, &ret, 3, args_eregReplace); + + zval instance; + object_init_ex(&instance, stringy_ce); + zend_call_method(&instance, stringy_ce, NULL, ZEND_STRL("__construct"), return_value, 2, &ret, encoding); + RETURN_ZVAL(&instance, 0, 1); +} +ZEND_BEGIN_ARG_INFO(arginfo_delimit, 0) + ZEND_ARG_INFO(0, delimiter) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, dasherize) +{ + zval delimit; + ZVAL_STRING(&delimit, "-"); + zval func, args[] = { + delimit, + }; + ZVAL_STRING(&func, "delimit"); + call_user_function(NULL, getThis(), &func, return_value, 1, args); +} + +PHP_METHOD(Stringy, endsWith) +{ + zval *substring, *caseSensitive = NULL; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(substring) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(caseSensitive) + ZEND_PARSE_PARAMETERS_END(); + + if (caseSensitive == NULL) { + caseSensitive = emalloc(sizeof(zval)); + ZVAL_BOOL(caseSensitive, IS_TRUE); + } + + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval substring_len; + zval func, args[] = { + *substring, + *encoding, + }; + ZVAL_STRING(&func, "mb_strlen"); + call_user_function(NULL, NULL, &func, &substring_len, 2, args); + + zval str_len; + ZVAL_STRING(&func, "length"); + call_user_function(NULL, getThis(), &func, &str_len, 0, NULL); + + zval start; + size_t str_length = Z_LVAL(str_len); + size_t substring_length = Z_LVAL(substring_len); + ZVAL_LONG(&start, str_length - substring_length); + zval startOfStr; + //\mb_substr($this->str, $strLength - $substringLength, $substringLength, $this->encoding); + zval args_substr[] = { + *str, + start, + substring_len, + *encoding, + }; + ZVAL_STRING(&func, "mb_substr"); + call_user_function(NULL, NULL, &func, &startOfStr, 4, args_substr); + + if (Z_TYPE_P(caseSensitive) == IS_FALSE) { + zval args_tolower_substring[] = { + *substring, + *encoding, + }; + ZVAL_STRING(&func, "mb_strtolower"); + call_user_function(NULL, NULL, &func, substring, 2, args_tolower_substring); + + zval args_tolower_startOfStr[] = { + startOfStr, + *encoding, + }; + ZVAL_STRING(&func, "mb_strtolower"); + call_user_function(NULL, NULL, &func, &startOfStr, 2, args_tolower_startOfStr); + } + + if (zend_string_equals(Z_STR_P(substring), Z_STR(startOfStr))) { + RETURN_BOOL(1); + } + + RETURN_BOOL(0); +} +ZEND_BEGIN_ARG_INFO(arginfo_endsWith, 0) + ZEND_ARG_INFO(0, substring) + ZEND_ARG_INFO(0, caseSensitive) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, endsWithAny) +{ + zval *substring, *caseSensitive = NULL; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(substring) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(caseSensitive) + ZEND_PARSE_PARAMETERS_END(); + + if (caseSensitive == NULL) { + caseSensitive = emalloc(sizeof(zval)); + ZVAL_BOOL(caseSensitive, IS_TRUE); + } + + convert_to_array(substring); + + zval *val; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(substring), val) { + zval func, args[] = { + *val, + *caseSensitive, + }; + ZVAL_STRING(&func, "endsWith"); + call_user_function(NULL, getThis(), &func, return_value, 2, args); + if (Z_TYPE_P(return_value) == IS_TRUE) { + RETURN_BOOL(1); + } + } ZEND_HASH_FOREACH_END(); + + RETURN_BOOL(0); +} +ZEND_BEGIN_ARG_INFO(arginfo_endsWithAny, 0) + ZEND_ARG_INFO(0, substring) + ZEND_ARG_INFO(0, caseSensitive) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, startsWith) +{ + zval *substring, *caseSensitive = NULL; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(substring) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(caseSensitive) + ZEND_PARSE_PARAMETERS_END(); + + if (caseSensitive == NULL) { + caseSensitive = emalloc(sizeof(zval)); + ZVAL_BOOL(caseSensitive, IS_TRUE); + } + + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval substring_len; + zval func, args[] = { + *substring, + *encoding, + }; + ZVAL_STRING(&func, "mb_strlen"); + call_user_function(NULL, NULL, &func, &substring_len, 2, args); + + zval start; + ZVAL_LONG(&start, 0); + zval startOfStr; + zval args_substr[] = { + *str, + start, + substring_len, + *encoding, + }; + ZVAL_STRING(&func, "mb_substr"); + call_user_function(NULL, NULL, &func, &startOfStr, 4, args_substr); + + if (Z_TYPE_P(caseSensitive) == IS_FALSE) { + zval args_tolower_substring[] = { + *substring, + *encoding, + }; + ZVAL_STRING(&func, "mb_strtolower"); + call_user_function(NULL, NULL, &func, substring, 2, args_tolower_substring); + + zval args_tolower_startOfStr[] = { + startOfStr, + *encoding, + }; + ZVAL_STRING(&func, "mb_strtolower"); + call_user_function(NULL, NULL, &func, &startOfStr, 2, args_tolower_startOfStr); + } + + if (zend_string_equals(Z_STR_P(substring), Z_STR(startOfStr))) { + RETURN_BOOL(1); + } + + RETURN_BOOL(0); +} +ZEND_BEGIN_ARG_INFO(arginfo_startsWith, 0) + ZEND_ARG_INFO(0, substring) + ZEND_ARG_INFO(0, caseSensitive) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, ensureLeft) +{ + zval *substring = NULL; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(substring) + ZEND_PARSE_PARAMETERS_END(); + + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval instance; + object_init_ex(&instance, stringy_ce); + zval func, args[] = { + *str, + *encoding, + }; + ZVAL_STRING(&func, "__construct"); + call_user_function(NULL, &instance, &func, return_value, 2, args); + + zval args_startWith[] = { + *substring + }; + ZVAL_STRING(&func, "startsWith"); + call_user_function(NULL, &instance, &func, return_value, 1, args_startWith); + + if (Z_TYPE_P(return_value) == IS_FALSE) { + zval tmp; + concat_function(&tmp, substring, str); + zend_update_property(stringy_ce, &instance, ZEND_STRL("str"), &tmp); + } + + RETURN_ZVAL(&instance, 0, 1); +} +ZEND_BEGIN_ARG_INFO(arginfo_ensureLeft, 0) + ZEND_ARG_INFO(0, substring) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, ensureRight) +{ + zval *substring = NULL; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(substring) + ZEND_PARSE_PARAMETERS_END(); + + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval instance; + object_init_ex(&instance, stringy_ce); + zval func, args[] = { + *str, + *encoding, + }; + ZVAL_STRING(&func, "__construct"); + call_user_function(NULL, &instance, &func, return_value, 2, args); + + zval args_startWith[] = { + *substring + }; + ZVAL_STRING(&func, "endsWith"); + call_user_function(NULL, &instance, &func, return_value, 1, args_startWith); + + if (Z_TYPE_P(return_value) == IS_FALSE) { + zval tmp; + concat_function(&tmp, str, substring); + zend_update_property(stringy_ce, &instance, ZEND_STRL("str"), &tmp); + } + + RETURN_ZVAL(&instance, 0, 1); +} +ZEND_BEGIN_ARG_INFO(arginfo_ensureRight, 0) + ZEND_ARG_INFO(0, substring) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, first) +{ + zval *n = NULL; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(n) + ZEND_PARSE_PARAMETERS_END(); + + if (n == NULL) { + n = emalloc(sizeof(zval)); + ZVAL_LONG(n, 0); + } + + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval instance; + object_init_ex(&instance, stringy_ce); + zval func, args[] = { + *str, + *encoding, + }; + ZVAL_STRING(&func, "__construct"); + call_user_function(NULL, &instance, &func, return_value, 2, args); + + zend_long len = Z_LVAL_P(n); + if (len < 0) { + zend_update_property_string(stringy_ce, &instance, ZEND_STRL("str"), ""); + RETURN_ZVAL(&instance, 0, 1); + } + + zval start; + ZVAL_LONG(&start, 0); + + zval args_substr[] = { + start, + *n, + }; + ZVAL_STRING(&func, "substr"); + call_user_function(NULL, &instance, &func, return_value, 2, args_substr); +} +ZEND_BEGIN_ARG_INFO(arginfo_first, 0) + ZEND_ARG_INFO(0, substring) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, last) +{ + zval *n = NULL; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(n) + ZEND_PARSE_PARAMETERS_END(); + + if (n == NULL) { + n = emalloc(sizeof(zval)); + ZVAL_LONG(n, 0); + } + + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval instance; + object_init_ex(&instance, stringy_ce); + zval func, args[] = { + *str, + *encoding, + }; + ZVAL_STRING(&func, "__construct"); + call_user_function(NULL, &instance, &func, return_value, 2, args); + + zend_long len = Z_LVAL_P(n); + if (len <= 0) { + zend_update_property_string(stringy_ce, &instance, ZEND_STRL("str"), ""); + RETURN_ZVAL(&instance, 0, 1); + } + + ZVAL_LONG(n, -Z_LVAL_P(n)); + + zval args_substr[] = { + *n, + }; + ZVAL_STRING(&func, "substr"); + call_user_function(NULL, &instance, &func, return_value, 1, args_substr); +} +ZEND_BEGIN_ARG_INFO(arginfo_last, 0) + ZEND_ARG_INFO(0, n) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, isAlpha) +{ + zval str_val; + ZVAL_STRING(&str_val, "^[[:alpha:]]*$"); + zval func, args[] = { + str_val + }; + ZVAL_STRING(&func, "matchesPattern"); + call_user_function(NULL, getThis(), &func, return_value, 1, args); +} + +PHP_METHOD(Stringy, matchesPattern) +{ + zval *pattern = NULL; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(pattern) + ZEND_PARSE_PARAMETERS_END(); + + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + + zval func, args[] = { + *pattern, + *str, + }; + ZVAL_STRING(&func, "mb_ereg_match"); + call_user_function(NULL, NULL, &func, return_value, 2, args); +} +ZEND_BEGIN_ARG_INFO(arginfo_matchesPattern, 0) + ZEND_ARG_INFO(0, pattern) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, isBlank) +{ + zval str_val; + ZVAL_STRING(&str_val, "^[[:space:]]*$"); + zval func, args[] = { + str_val + }; + ZVAL_STRING(&func, "matchesPattern"); + call_user_function(NULL, getThis(), &func, return_value, 1, args); +} + +PHP_METHOD(Stringy, hasLowerCase) +{ + zval str_val; + ZVAL_STRING(&str_val, ".*[[:lower:]]"); + zval func, args[] = { + str_val + }; + ZVAL_STRING(&func, "matchesPattern"); + call_user_function(NULL, getThis(), &func, return_value, 1, args); +} + +PHP_METHOD(Stringy, hasUpperCase) +{ + zval str_val; + ZVAL_STRING(&str_val, ".*[[:upper:]]"); + zval func, args[] = { + str_val + }; + ZVAL_STRING(&func, "matchesPattern"); + call_user_function(NULL, getThis(), &func, return_value, 1, args); +} + +PHP_METHOD(Stringy, htmlDecode) +{ + zval *flags = NULL; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(flags); + ZEND_PARSE_PARAMETERS_END(); + + if (flags == NULL) { + ZVAL_STRING(flags, "ENT_COMPAT"); + } + + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval func, args[] = { + *str, + *flags, + *encoding, + }; + ZVAL_STRING(&func, "html_entity_decode"); + call_user_function(NULL, NULL, &func, return_value, 3, args); + + + zval instance; + object_init_ex(&instance, stringy_ce); + zval func_init, args_init[] = { + *return_value, + *encoding, + }; + ZVAL_STRING(&func_init, "__construct"); + call_user_function(NULL, &instance, &func_init, return_value, 2, args_init); + + RETURN_ZVAL(&instance, 0, 1); +} +ZEND_BEGIN_ARG_INFO(arginfo_htmlDecode, 0) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, htmlEncode) +{ + zval *flags = NULL; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(flags); + ZEND_PARSE_PARAMETERS_END(); + + if (flags == NULL) { + ZVAL_STRING(flags, "ENT_COMPAT"); + } + + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval func, args[] = { + *str, + *flags, + *encoding, + }; + ZVAL_STRING(&func, "htmlentities"); + call_user_function(NULL, NULL, &func, return_value, 3, args); + + + zval instance; + object_init_ex(&instance, stringy_ce); + zval func_init, args_init[] = { + *return_value, + *encoding, + }; + ZVAL_STRING(&func_init, "__construct"); + call_user_function(NULL, &instance, &func_init, return_value, 2, args_init); + + RETURN_ZVAL(&instance, 0, 1); +} +ZEND_BEGIN_ARG_INFO(arginfo_htmlEncode, 0) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, removeLeft) +{ + zval *substring = NULL; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(substring); + ZEND_PARSE_PARAMETERS_END(); + + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval instance; + object_init_ex(&instance, stringy_ce); + zval func_init, args_init[] = { + *str, + *encoding, + }; + ZVAL_STRING(&func_init, "__construct"); + call_user_function(NULL, &instance, &func_init, return_value, 2, args_init); + + zval func, args[] = { + *substring, + }; + ZVAL_STRING(&func, "startsWith"); + call_user_function(NULL, &instance, &func, return_value, 1, args); + + if (Z_TYPE_P(return_value) == IS_TRUE) { + zval func_strlen, args_strlen[] = { + *substring, + *encoding, + }; + ZVAL_STRING(&func_strlen, "mb_strlen"); + call_user_function(NULL, NULL, &func_strlen, return_value, 2, args_strlen); + + zval func_substr, args_substr[] = { + *return_value, + }; + ZVAL_STRING(&func_substr, "substr"); + call_user_function(NULL, &instance, &func_substr, return_value, 1, args_substr); + return; + } + + RETURN_ZVAL(&instance, 0, 1); +} +ZEND_BEGIN_ARG_INFO(arginfo_removeLeft, 0) + ZEND_ARG_INFO(0, substring) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, removeRight) +{ + zval *substring = NULL; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(substring); + ZEND_PARSE_PARAMETERS_END(); + + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval instance; + object_init_ex(&instance, stringy_ce); + zval func_init, args_init[] = { + *str, + *encoding, + }; + ZVAL_STRING(&func_init, "__construct"); + call_user_function(NULL, &instance, &func_init, return_value, 2, args_init); + + zval func, args[] = { + *substring, + }; + ZVAL_STRING(&func, "endsWith"); + call_user_function(NULL, &instance, &func, return_value, 1, args); + + if (Z_TYPE_P(return_value) == IS_TRUE) { + zval func_strlen, args_strlen[] = { + *substring, + *encoding, + }; + ZVAL_STRING(&func_strlen, "mb_strlen"); + call_user_function(NULL, NULL, &func_strlen, return_value, 2, args_strlen); + size_t substing_len = Z_LVAL_P(return_value); + + zval func_len; + ZVAL_STRING(&func_len, "length"); + call_user_function(NULL, getThis(), &func_len, return_value, 0, NULL); + size_t len = Z_LVAL_P(return_value); + + + zval zero; + zval second; + ZVAL_LONG(&zero, 0); + ZVAL_LONG(&second, len - substing_len); + zval func_substr, args_substr[] = { + zero, + second, + }; + ZVAL_STRING(&func_substr, "substr"); + call_user_function(NULL, getThis(), &func_substr, return_value, 2, args_substr); + return; + } + + RETURN_ZVAL(&instance, 0, 1); +} +ZEND_BEGIN_ARG_INFO(arginfo_removeRight, 0) + ZEND_ARG_INFO(0, substring) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, humanize) +{ + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval search; + array_init(&search); + add_next_index_string(&search, "_id"); + add_next_index_string(&search, "_"); + + zval repalce; + array_init(&repalce); + add_next_index_string(&repalce, ""); + add_next_index_string(&repalce, " "); + + zval func, args[] = { + search, + repalce, + *str, + }; + ZVAL_STRING(&func, "str_replace"); + call_user_function(NULL, NULL, &func, return_value, 3, args); + + zval instance; + object_init_ex(&instance, stringy_ce); + zval func_construct, args_construct[] = { + *return_value, + *encoding, + }; + ZVAL_STRING(&func_construct, "__construct"); + call_user_function(NULL, &instance, &func_construct, return_value, 2, args_construct); + + zval func_trim; + ZVAL_STRING(&func_trim, "trim"); + call_user_function(NULL, &instance, &func_trim, return_value, 0, NULL); + + zval func_upperCaseFirst; + ZVAL_STRING(&func_upperCaseFirst, "upperCaseFirst"); + call_user_function(NULL, return_value, &func_upperCaseFirst, return_value, 0, NULL); + + RETURN_ZVAL(return_value, 0, 1); +} + +PHP_METHOD(Stringy, insert) +{ + zval *substring, *index; + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(substring) + Z_PARAM_ZVAL(index) + ZEND_PARSE_PARAMETERS_END(); + + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval instance; + object_init_ex(&instance, stringy_ce); + zval func, args[] = { + *str, + *encoding, + }; + ZVAL_STRING(&func, "__construct"); + call_user_function(NULL, &instance, &func, return_value, 2, args); + + + zval len; + zval func_length; + ZVAL_STRING(&func_length, "length"); + call_user_function(NULL, &instance, &func_length, &len, 0, NULL); + + if (Z_LVAL_P(index) > Z_LVAL(len)) { + RETURN_ZVAL(&instance, 0, 1); + } + + zval zero; + ZVAL_LONG(&zero, 0); + + zval start; + zval func_substr1, args_substr1[] = { + *str, + zero, + *index, + *encoding, + }; + ZVAL_STRING(&func_substr1, "mb_substr"); + call_user_function(NULL, NULL, &func_substr1, &start, 4, args_substr1); + + zval end; + zval func_substr2, args_substr2[] = { + *str, + *index, + len, + *encoding, + }; + ZVAL_STRING(&func_substr2, "mb_substr"); + call_user_function(NULL, NULL, &func_substr2, &end, 4, args_substr2); + + zval ret; + concat_function(&ret, &start, substring); + concat_function(&ret, &ret, &end); + + zend_update_property(stringy_ce, &instance, ZEND_STRL("str"), &ret); + + RETURN_ZVAL(&instance, 0, 1); +} +ZEND_BEGIN_ARG_INFO(arginfo_insert, 0) + ZEND_ARG_INFO(0, substring) + ZEND_ARG_INFO(0, index) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, isAlphanumeric) +{ + zval str_val; + ZVAL_STRING(&str_val, "^[[:alnum:]]*$"); + zval func, args[] = { + str_val + }; + ZVAL_STRING(&func, "matchesPattern"); + call_user_function(NULL, getThis(), &func, return_value, 1, args); +} + +PHP_METHOD(Stringy, isLowerCase) +{ + zval str_val; + ZVAL_STRING(&str_val, "^[[:lower:]]*$"); + zval func, args[] = { + str_val + }; + ZVAL_STRING(&func, "matchesPattern"); + call_user_function(NULL, getThis(), &func, return_value, 1, args); +} + +PHP_METHOD(Stringy, isUpperCase) +{ + zval str_val; + ZVAL_STRING(&str_val, "^[[:upper:]]*$"); + zval func, args[] = { + str_val + }; + ZVAL_STRING(&func, "matchesPattern"); + call_user_function(NULL, getThis(), &func, return_value, 1, args); +} + +PHP_METHOD(Stringy, isHexadecimal) +{ + zval str_val; + ZVAL_STRING(&str_val, "^[[:xdigit:]]*$"); + zval func, args[] = { + str_val + }; + ZVAL_STRING(&func, "matchesPattern"); + call_user_function(NULL, getThis(), &func, return_value, 1, args); +} + +PHP_METHOD(Stringy, isJson) +{ + zval func; + ZVAL_STRING(&func, "length"); + call_user_function(NULL, getThis(), &func, return_value, 0, NULL); + if (Z_LVAL_P(return_value) <= 0) { + RETURN_BOOL(0); + } + + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + + zval func_json_decode, args[] = { + *str, + }; + ZVAL_STRING(&func_json_decode, "json_decode"); + call_user_function(NULL, NULL, &func_json_decode, return_value, 1, args); + + ZVAL_STRING(&func, "json_last_error"); + call_user_function(NULL, NULL, &func, return_value, 0, NULL); + + zval *json_error_none = zend_get_constant_str(ZEND_STRL("JSON_ERROR_NONE")); + + if (Z_LVAL_P(json_error_none) == Z_LVAL_P(return_value)) { + RETURN_BOOL(1); + } + RETURN_BOOL(0); +} + +PHP_METHOD(Stringy, isSerialized) +{ + zval str_object_serialized; + ZVAL_STRING(&str_object_serialized, "b:0;"); + + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + + if (Z_TYPE_P(str) == Z_TYPE(str_object_serialized) && Z_STR_P(str) == Z_STR(str_object_serialized)) + { + RETURN_TRUE; + } + zval func; + + zval error_level; + ZVAL_STRING(&func, "error_reporting"); + zval new_error_level; + ZVAL_LONG(&new_error_level, 0); + zval args_error_reporting[] = { + new_error_level, + }; + call_user_function(NULL, NULL, &func, &error_level, 1, args_error_reporting); + + zval args[] = { + *str, + }; + ZVAL_STRING(&func, "unserialize"); + call_user_function(NULL, NULL, &func, return_value, 1, args); + + zval reset_error_level; + zval args_reset[] = { + error_level, + }; + ZVAL_STRING(&func, "error_reporting"); + call_user_function(NULL, NULL, &func, &reset_error_level, 1, args_reset); + + if (Z_TYPE_P(return_value) != IS_FALSE) { + RETURN_TRUE; + } + + RETURN_FALSE; +} + +PHP_METHOD(Stringy, isBase64) +{ + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + + zval z_true; + ZVAL_BOOL(&z_true, IS_TRUE); + zval func, args[] = { + *str, + z_true, + }; + ZVAL_STRING(&func, "base64_decode"); + call_user_function(NULL, NULL, &func, return_value, 2, args); + + zval args_decode[] = { + *return_value, + }; + ZVAL_STRING(&func, "base64_encode"); + call_user_function(NULL, NULL, &func, return_value, 1, args_decode); + + if (zval_str_equal(return_value, str)) { + RETURN_TRUE; + } + + RETURN_FALSE; +} + +PHP_METHOD(Stringy, longestCommonPrefix) +{ + zval *otherStr; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(otherStr) + ZEND_PARSE_PARAMETERS_END(); + + zval rv; + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval func; + ZVAL_STRING(&func, "length"); + call_user_function(NULL, getThis(), &func, return_value, 0, NULL); + size_t length = Z_LVAL_P(return_value); + + zval args_mb_strlen[] = { + *otherStr, + *encoding, + }; + ZVAL_STRING(&func, "mb_strlen"); + call_user_function(NULL, NULL, &func, return_value, 2, args_mb_strlen); + size_t mb_strlen_length = Z_LVAL_P(return_value); + + size_t maxLength = MIN(length, mb_strlen_length); + + zval longestCommonPrefix, i_tmp, const_substr_len, ret_first, ret_second; + ZVAL_EMPTY_STRING(&longestCommonPrefix); + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + ZVAL_LONG(&const_substr_len, 1); + + for(int i = 0; i < maxLength; i++) { + ZVAL_LONG(&i_tmp, i); + zval args_mb_substr[] = { + *str, + i_tmp, + const_substr_len, + *encoding, + }; + ZVAL_STRING(&func, "mb_substr"); + call_user_function(NULL, NULL, &func, &ret_first, 4, args_mb_substr); + + zval args_mb_substr2[] = { + *otherStr, + i_tmp, + const_substr_len, + *encoding, + }; + ZVAL_STRING(&func, "mb_substr"); + call_user_function(NULL, NULL, &func, &ret_second, 4, args_mb_substr2); + + if (zend_string_equals(Z_STR(ret_first), Z_STR(ret_second)) == false) { + break; + } + + concat_function(&longestCommonPrefix, &longestCommonPrefix, &ret_first); + } + + zval instance; + object_init_ex(&instance, stringy_ce); + + zval args_const[] = { + longestCommonPrefix, + *encoding, + }; + ZVAL_STRING(&func, "__construct"); + call_user_function(NULL, &instance, &func, return_value, 2, args_const); + + RETURN_ZVAL(&instance, 0, 1); +} +ZEND_BEGIN_ARG_INFO(arginfo_longestCommonPrefix, 0) + ZEND_ARG_INFO(0, otherStr) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, longestCommonSuffix) +{ + zval *otherStr; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(otherStr) + ZEND_PARSE_PARAMETERS_END(); + + zval rv; + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval func; + ZVAL_STRING(&func, "length"); + call_user_function(NULL, getThis(), &func, return_value, 0, NULL); + size_t length = Z_LVAL_P(return_value); + + zval args_mb_strlen[] = { + *otherStr, + *encoding, + }; + ZVAL_STRING(&func, "mb_strlen"); + call_user_function(NULL, NULL, &func, return_value, 2, args_mb_strlen); + size_t mb_strlen_length = Z_LVAL_P(return_value); + + size_t maxLength = MIN(length, mb_strlen_length); + + zval longestCommonSuffix, i_tmp, const_substr_len, ret_first, ret_second; + ZVAL_EMPTY_STRING(&longestCommonSuffix); + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + ZVAL_LONG(&const_substr_len, 1); + + for(int i = 1; i <= maxLength; i++) { + ZVAL_LONG(&i_tmp, -i); + zval args_mb_substr[] = { + *str, + i_tmp, + const_substr_len, + *encoding, + }; + ZVAL_STRING(&func, "mb_substr"); + call_user_function(NULL, NULL, &func, &ret_first, 4, args_mb_substr); + + zval args_mb_substr2[] = { + *otherStr, + i_tmp, + const_substr_len, + *encoding, + }; + ZVAL_STRING(&func, "mb_substr"); + call_user_function(NULL, NULL, &func, &ret_second, 4, args_mb_substr2); + + if (zend_string_equals(Z_STR(ret_first), Z_STR(ret_second)) == false) { + break; + } + zval tmp; + concat_function(&tmp, &ret_first, &longestCommonSuffix); + longestCommonSuffix = tmp; + } + + zval instance; + object_init_ex(&instance, stringy_ce); + + zval args_const[] = { + longestCommonSuffix, + *encoding, + }; + ZVAL_STRING(&func, "__construct"); + call_user_function(NULL, &instance, &func, return_value, 2, args_const); + + RETURN_ZVAL(&instance, 0, 1); +} +ZEND_BEGIN_ARG_INFO(arginfo_longestCommonSuffix, 0) + ZEND_ARG_INFO(0, otherStr) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, longestCommonSubstring) +{ + // Uses dynamic programming to solve + // http://en.wikipedia.org/wiki/Longest_common_substring_problem + + zval *otherStr; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(otherStr) + ZEND_PARSE_PARAMETERS_END(); + + zval rv; + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + + zval instance, func; + object_init_ex(&instance, stringy_ce); + + zval args_const[] = { + *str, + *encoding, + }; + ZVAL_STRING(&func, "__construct"); + call_user_function(NULL, &instance, &func, return_value, 2, args_const); + + zval strLen; + ZVAL_STRING(&func, "length"); + call_user_function(NULL, &instance, &func, &strLen, 0, NULL); + + zval otherLength, args_mb_strlen[] = { + *otherStr, + *encoding, + }; + ZVAL_STRING(&func, "mb_strlen"); + call_user_function(NULL, NULL, &func, &otherLength, 2, args_mb_strlen); + + if (Z_LVAL(strLen) == 0 || Z_LVAL(otherLength) == 0) { + zend_update_property_stringl(stringy_ce, &instance, ZEND_STRL("str"), ZEND_STRL("")); + RETURN_ZVAL(&instance, 0, 1); + } + + zval len, end, table, tmp, zero; + ZVAL_LONG(&len, 0); + ZVAL_LONG(&end, 0); + ZVAL_LONG(&zero, 0); + ZVAL_LONG(&otherLength, Z_LVAL(otherLength)+1); + ZVAL_LONG(&strLen, Z_LVAL(strLen)+1); + array_init(&table); + + zval args_array_fill[] = { + zero, + otherLength, + zero, + }; + ZVAL_STRING(&func, "array_fill"); + call_user_function(NULL, NULL, &func, return_value, 3, args_array_fill); + + zval args_array_fill2[] = { + zero, + strLen, + *return_value, + }; + call_user_function(NULL, NULL, &func, &table, 3, args_array_fill2); + + zval strChar, otherChar, i_tmp, j_tmp, const_substr_len; + ZVAL_LONG(&const_substr_len, 1); + for(int i = 1; i < Z_LVAL(strLen); i++) { + + zend_array *ht = Z_ARRVAL(table); + for(int j = 1; j < Z_LVAL(otherLength); j++) { + ZVAL_LONG(&i_tmp, i - 1); + zval args_mb_strlen[] = { + *str, + i_tmp, + const_substr_len, + *encoding, + }; + ZVAL_STRING(&func, "mb_substr"); + call_user_function(NULL, NULL, &func, &strChar, 4, args_mb_strlen); + + ZVAL_LONG(&j_tmp, j - 1); + zval args_mb_strlen2[] = { + *otherStr, + j_tmp, + const_substr_len, + *encoding, + }; + ZVAL_STRING(&func, "mb_substr"); + call_user_function(NULL, NULL, &func, &otherChar, 4, args_mb_strlen2); + + zval *tmp = zend_hash_index_find(ht, i); + if (zend_string_equals(Z_STR(otherChar), Z_STR(strChar))) { + zval *tmp2 = zend_hash_index_find(ht, i - 1); + tmp2 = zend_hash_index_find(Z_ARRVAL_P(tmp2), j - 1); + zval value; + ZVAL_LONG(&value, Z_LVAL_P(tmp2) + 1); + + zval_add_ref(&value); + zend_hash_index_update(Z_ARR_P(tmp), j, &value); + + if (Z_LVAL(value) > Z_LVAL(len)) { + ZVAL_LONG(&len, Z_LVAL(value)); + ZVAL_LONG(&end, i); + } + } else { + zval_add_ref(&zero); + zend_hash_index_update(Z_ARR_P(tmp), j, &zero); + } + + zval_add_ref(tmp); + zend_hash_index_update(ht, j, tmp); + } + } + + zval end_len; + ZVAL_LONG(&end_len, Z_LVAL(end) - Z_LVAL(len)); + + zval args_mb_strlen3[] = { + *str, + end_len, + len, + *encoding, + }; + ZVAL_STRING(&func, "mb_substr"); + call_user_function(NULL, NULL, &func, return_value, 4, args_mb_strlen3); + + zend_update_property(stringy_ce, &instance, ZEND_STRL("str"), return_value); + + RETURN_ZVAL(&instance, 0, 1); +} +ZEND_BEGIN_ARG_INFO(arginfo_longestCommonSubstring, 0) + ZEND_ARG_INFO(0, otherStr) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, pad) +{ + zval *length, *padStr, *padType; + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_ZVAL(length) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(padStr) + Z_PARAM_ZVAL(padType) + ZEND_PARSE_PARAMETERS_END(); + + if (padStr == NULL) { + padStr = emalloc(sizeof(zval)); + ZVAL_STRING(padStr, " "); + } + + if (padType == NULL) { + padType = emalloc(sizeof(zval)); + ZVAL_STRING(padType, "right"); + } + + zval left, right, both; + ZVAL_STRING(&left, "left"); + ZVAL_STRING(&right, "right"); + ZVAL_STRING(&both, "both"); + + zval func; + + if (zval_str_equal(&left, padType)) { + ZVAL_STRING(&func, "padLeft"); + } else if (zval_str_equal(&right, padType) == 1) { + ZVAL_STRING(&func, "padRight"); + } else if (zval_str_equal(&both, padType) == 1) { + ZVAL_STRING(&func, "padBoth"); + } else { + zend_throw_exception_ex( + spl_ce_InvalidArgumentException, + 0, + "Pad expects %s to be one of 'left', 'right' or 'both'", + Z_STRVAL_P(padType) + ); + } + + zval args[] = { + *length, + *padStr + }; + + call_user_function(NULL, getThis(), &func, return_value, 2, args); +} +ZEND_BEGIN_ARG_INFO(arginfo_pad, 0) + ZEND_ARG_INFO(0, length) + ZEND_ARG_INFO(0, padStr) + ZEND_ARG_INFO(0, padType) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, applyPadding) +{ + zval *left, *right, *padStr; + ZEND_PARSE_PARAMETERS_START(0, 3) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(left); + Z_PARAM_ZVAL(right); + Z_PARAM_ZVAL(padStr); + ZEND_PARSE_PARAMETERS_END(); + + zval rv, zero; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 1, &rv); + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 1, &rv); + + ZVAL_LONG(&zero, 0); + + if (left == NULL) { + left = emalloc(sizeof(zval)); + ZVAL_LONG(left, 0); + } + + if (Z_TYPE_P(left) != IS_LONG) { + convert_to_long(left); + } + + if (right == NULL) { + right = emalloc(sizeof(zval)); + ZVAL_LONG(right, 0); + } + + if (Z_TYPE_P(right) != IS_LONG) { + convert_to_long(right); + } + + if (padStr == NULL) { + padStr = emalloc(sizeof(zval)); + ZVAL_STRING(padStr, " "); + } + + zval stringy; + object_init_ex(&stringy, stringy_ce); + zval func, args[] = { + *str, + *encoding, + }; + ZVAL_STRING(&func, "__construct"); + call_user_function(NULL, &stringy, &func, return_value, 2, args); + + zval length; + zval args_mb_strlen[] = { + *padStr, + *encoding, + }; + ZVAL_STRING(&func, "mb_strlen"); + call_user_function(NULL, NULL, &func, &length, 2, args_mb_strlen); + + zval strLength; + ZVAL_STRING(&func, "length"); + call_user_function(NULL, &stringy, &func, return_value, 0, NULL); + + zval paddedLength; + ZVAL_LONG(&paddedLength, Z_LVAL(strLength) + Z_LVAL_P(left) + Z_LVAL_P(right)); + + if (Z_TYPE(length) == IS_FALSE || Z_LVAL(paddedLength) <= Z_LVAL(strLength)) { + RETURN_ZVAL(&stringy, 0, 1); + } + + zval str_repeat_0; + zval str_repeat_0_val; + ZVAL_LONG(&str_repeat_0_val, ceil(Z_LVAL_P(left)/(float)Z_LVAL(length))); + zval leftPadding; + if (Z_LVAL(str_repeat_0_val) >=0) + { + zval args_str_repeat[] = { + *padStr, + str_repeat_0_val, + }; + ZVAL_STRING(&func, "str_repeat"); + call_user_function(NULL, NULL, &func, &str_repeat_0, 2, args_str_repeat); + + zval args_leftPadding[] = { + str_repeat_0, + zero, + *left, + *encoding, + }; + ZVAL_STRING(&func, "mb_substr"); + call_user_function(NULL, NULL, &func, &leftPadding, 4, args_leftPadding); + } else { + ZVAL_STRING(&leftPadding, ""); + } + + zval str_repeat_1; + zval str_repeat_1_val; + ZVAL_LONG(&str_repeat_1_val, ceil(Z_LVAL_P(right)/(float)Z_LVAL(length))); + zval rightPadding; + if (Z_LVAL(str_repeat_1_val) >=0) + { + zval args_str_repeat_1[] = { + *padStr, + str_repeat_1_val, + }; + ZVAL_STRING(&func, "str_repeat"); + call_user_function(NULL, NULL, &func, &str_repeat_1, 2, args_str_repeat_1); + + zval args_rightPadding[] = { + str_repeat_1, + zero, + *right, + *encoding, + }; + ZVAL_STRING(&func, "mb_substr"); + call_user_function(NULL, NULL, &func, &rightPadding, 4, args_rightPadding); + } else { + ZVAL_STRING(&rightPadding, ""); + } + + zval x; + concat_function(&x, &leftPadding, str); + concat_function(&x, &x, &rightPadding); + + RETURN_ZVAL(&x, 0, 1); +} +ZEND_BEGIN_ARG_INFO(arginfo_applyPadding, 0) + ZEND_ARG_INFO(0, left) + ZEND_ARG_INFO(0, right) + ZEND_ARG_INFO(0, padStr) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, padLeft) +{ + zval *length, *padStr; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(length) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(padStr) + ZEND_PARSE_PARAMETERS_END(); + + if (padStr == NULL) { + padStr = emalloc(sizeof(zval)); + ZVAL_STRING(padStr, " "); + } + + zval zero; + ZVAL_LONG(&zero, 0); + + zval func; + ZVAL_STRING(&func, "length"); + call_user_function(NULL, getThis(), &func, return_value, 0, NULL); + + zval first; + ZVAL_LONG(&first, Z_LVAL_P(length) - Z_LVAL_P(return_value)); + zval args[] = { + first, + zero, + *padStr, + }; + ZVAL_STRING(&func, "applyPadding"); + call_user_function(NULL, getThis(), &func, return_value, 3, args); +} +ZEND_BEGIN_ARG_INFO(arginfo_padLeft, 0) + ZEND_ARG_INFO(0, length) + ZEND_ARG_INFO(0, padStr) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, padRight) +{ + zval *length, *padStr; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(length) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(padStr) + ZEND_PARSE_PARAMETERS_END(); + + if (padStr == NULL) { + padStr = emalloc(sizeof(zval)); + ZVAL_STRING(padStr, " "); + } + + zval zero; + ZVAL_LONG(&zero, 0); + + zval func; + ZVAL_STRING(&func, "length"); + call_user_function(NULL, getThis(), &func, return_value, 0, NULL); + + zval value; + ZVAL_LONG(&value, Z_LVAL_P(length) - Z_LVAL_P(return_value)); + zval args[] = { + zero, + value, + *padStr, + }; + ZVAL_STRING(&func, "applyPadding"); + call_user_function(NULL, getThis(), &func, return_value, 3, args); +} +ZEND_BEGIN_ARG_INFO(arginfo_padRight, 0) + ZEND_ARG_INFO(0, length) + ZEND_ARG_INFO(0, padStr) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, padBoth) +{ + zval *length, *padStr; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(length) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(padStr) + ZEND_PARSE_PARAMETERS_END(); + + if (padStr == NULL) { + padStr = emalloc(sizeof(zval)); + ZVAL_STRING(padStr, " "); + } + + zval func; + ZVAL_STRING(&func, "length"); + call_user_function(NULL, getThis(), &func, return_value, 0, NULL); + + zval value; + ZVAL_LONG(&value, Z_LVAL_P(length) - Z_LVAL_P(return_value)); + + zval first, second; + ZVAL_LONG(&first, floor(Z_LVAL(value) / 2.0)); + ZVAL_LONG(&second, ceil(Z_LVAL(value) / 2.0);) + + zval args[] = { + first, + second, + *padStr, + }; + ZVAL_STRING(&func, "applyPadding"); + call_user_function(NULL, getThis(), &func, return_value, 3, args); +} +ZEND_BEGIN_ARG_INFO(arginfo_padBoth, 0) + ZEND_ARG_INFO(0, length) + ZEND_ARG_INFO(0, padStr) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, repeat) +{ + zval *multilpier; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(multilpier) + ZEND_PARSE_PARAMETERS_END(); + + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + zval func, args[] = { + *str, + *multilpier, + }; + ZVAL_STRING(&func, "str_repeat"); + call_user_function(NULL, NULL, &func, return_value, 2, args); + + zval instance; + object_init_ex(&instance, stringy_ce); + + zval args_construct[] = { + *return_value, + *encoding, + }; + ZVAL_STRING(&func, "__construct"); + call_user_function(NULL, &instance, &func, return_value, 2, args_construct); + + RETURN_ZVAL(&instance, 0, 1); +} +ZEND_BEGIN_ARG_INFO(arginfo_repeat, 0) + ZEND_ARG_INFO(0, multiplier) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, replace) +{ + zval *search, *replacement; + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(search) + Z_PARAM_ZVAL(replacement) + ZEND_PARSE_PARAMETERS_END(); + + zval func, delimiter; + ZVAL_STRING(&func, "preg_quote"); + zval args[] = { + *search, + }; + call_user_function(NULL, NULL, &func, return_value, 1, args); + + ZVAL_STRING(&func, "regexReplace"); + zval args_regex[] = { + *return_value, + *replacement, + }; + call_user_function(NULL, getThis(), &func, return_value, 2, args_regex); +} +ZEND_BEGIN_ARG_INFO(arginfo_replace, 0) + ZEND_ARG_INFO(0, search) + ZEND_ARG_INFO(0, replacement) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, toAscii) +{ + zval *language, *removeUnsupported; + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(language) + Z_PARAM_ZVAL(removeUnsupported) + ZEND_PARSE_PARAMETERS_END(); + + if (language == NULL) { + language = (zval *) emalloc(sizeof(zval)); + ZVAL_STRING(language, "en"); + } + + if (removeUnsupported == NULL) { + removeUnsupported = (zval *) emalloc(sizeof(zval)); + ZVAL_BOOL(removeUnsupported, IS_TRUE); + } + + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + zval tmp = *str; + + zval func, args[] = { + *language + }; + ZVAL_STRING(&func, "langSpecificCharsArray"); + call_user_function(NULL, getThis(), &func, return_value, 1, args); + + zval chars; + if (zend_array_count(Z_ARRVAL_P(return_value)) > 0) { + zval args_preg_replace_1[] = { + *zend_hash_index_find(Z_ARRVAL_P(return_value), 0), + *zend_hash_index_find(Z_ARRVAL_P(return_value), 1), + *str, + }; + ZVAL_STRING(&func, "str_replace"); + call_user_function(NULL, NULL, &func, &tmp, 3, args_preg_replace_1); + } + + ZVAL_STRING(&func, "charsArray"); + call_user_function(NULL, getThis(), &func, &chars, 0, NULL); + + zval *value; + zend_string *key; + zval x; + ulong idx; + + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(chars), idx, key, value) { + if (!key) { + key = zend_long_to_str(idx); + } + ZVAL_NEW_STR(&x, key); + + zval args_preg_replace_2[] = { + *value, + x, + tmp, + }; + ZVAL_STRING(&func, "str_replace"); + + call_user_function(NULL, NULL, &func, &tmp, 3, args_preg_replace_2); + } ZEND_HASH_FOREACH_END(); + + if (Z_TYPE_P(removeUnsupported) == IS_TRUE) { + zval pattern, replacement; + ZVAL_STRING(&pattern, "/[^\x20-\x7E]/u"); + ZVAL_STRING(&replacement, ""); + + zval args_preg_replace_3[] = { + pattern, + replacement, + tmp, + }; + ZVAL_STRING(&func, "preg_replace"); + call_user_function(NULL, NULL, &func, &tmp, 3, args_preg_replace_3); + } + + zval instance; + object_init_ex(&instance, stringy_ce); + + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval args_construct[] = { + tmp, + *encoding, + }; + ZVAL_STRING(&func, "__construct"); + call_user_function(NULL, &instance, &func, return_value, 2, args_construct); + + RETURN_ZVAL(&instance, 0, 1); +} +ZEND_BEGIN_ARG_INFO(arginfo_toAscii, 0) + ZEND_ARG_INFO(0, language) + ZEND_ARG_INFO(0, removeUnsupported) +ZEND_END_ARG_INFO(); + +//TODO: 先实现再说 +PHP_METHOD(Stringy, charsArray) +{ + zval ret; + array_init(&ret); + + char str_arr[STRINNGY_ROW_COUNT][STRINNGY_COLUM_COUNT][10] = { + {"°", "₀", "۰", "0"}, + {"¹", "₁", "۱", "1"}, + {"²", "₂", "۲", "2"}, + {"³", "₃", "۳", "3"}, + {"⁴", "₄", "۴", "٤", "4"}, + {"⁵", "₅", "۵", "٥", "5"}, + {"⁶", "₆", "۶", "٦", "6"}, + {"⁷", "₇", "۷", "7"}, + {"⁸", "₈", "۸", "8"}, + {"⁹", "₉", "۹", "9"}, + {"à", "á", "ả", "ã", "ạ", "ă", "ắ", "ằ", "ẳ", "ẵ", + "ặ", "â", "ấ", "ầ", "ẩ", "ẫ", "ậ", "ā", "ą", "å", + "α", "ά", "ἀ", "ἁ", "ἂ", "ἃ", "ἄ", "ἅ", "ἆ", "ἇ", + "ᾀ", "ᾁ", "ᾂ", "ᾃ", "ᾄ", "ᾅ", "ᾆ", "ᾇ", "ὰ", "ά", + "ᾰ", "ᾱ", "ᾲ", "ᾳ", "ᾴ", "ᾶ", "ᾷ", "а", "أ", "အ", + "ာ", "ါ", "ǻ", "ǎ", "ª", "ა", "अ", "ا", "a", "ä"}, + {"б", "β", "ب", "ဗ", "ბ", "b"}, + {"ç", "ć", "č", "ĉ", "ċ", "c"}, + {"ď", "ð", "đ", "ƌ", "ȡ", "ɖ", "ɗ", "ᵭ", "ᶁ", "ᶑ", + "д", "δ", "د", "ض", "ဍ", "ဒ", "დ", "d"}, + {"é", "è", "ẻ", "ẽ", "ẹ", "ê", "ế", "ề", "ể", "ễ", + "ệ", "ë", "ē", "ę", "ě", "ĕ", "ė", "ε", "έ", "ἐ", + "ἑ", "ἒ", "ἓ", "ἔ", "ἕ", "ὲ", "έ", "е", "ё", "э", + "є", "ə", "ဧ", "ေ", "ဲ", "ე", "ए", "إ", "ئ", "e"}, + {"ф", "φ", "ف", "ƒ", "ფ", "f"}, + {"ĝ", "ğ", "ġ", "ģ", "г", "ґ", "γ", "ဂ", "გ", "گ", + "g"}, + {"ĥ", "ħ", "η", "ή", "ح", "ه", "ဟ", "ှ", "ჰ", "h"}, + {"í", "ì", "ỉ", "ĩ", "ị", "î", "ï", "ī", "ĭ", "į", + "ı", "ι", "ί", "ϊ", "ΐ", "ἰ", "ἱ", "ἲ", "ἳ", "ἴ", + "ἵ", "ἶ", "ἷ", "ὶ", "ί", "ῐ", "ῑ", "ῒ", "ΐ", "ῖ", + "ῗ", "і", "ї", "и", "ဣ", "ိ", "ီ", "ည်", "ǐ", "ი", + "इ", "ی", "i"}, + {"ĵ", "ј", "Ј", "ჯ", "ج", "j"}, + {"ķ", "ĸ", "к", "κ", "Ķ", "ق", "ك", "က", "კ", "ქ", + "ک", "k"}, + {"ł", "ľ", "ĺ", "ļ", "ŀ", "л", "λ", "ل", "လ", "ლ", + "l"}, + {"м", "μ", "م", "မ", "მ", "m"}, + {"ñ", "ń", "ň", "ņ", "ʼn", "ŋ", "ν", "н", "ن", "န", + "ნ", "n"}, + {"ó", "ò", "ỏ", "õ", "ọ", "ô", "ố", "ồ", "ổ", "ỗ", + "ộ", "ơ", "ớ", "ờ", "ở", "ỡ", "ợ", "ø", "ō", "ő", + "ŏ", "ο", "ὀ", "ὁ", "ὂ", "ὃ", "ὄ", "ὅ", "ὸ", "ό", + "о", "و", "θ", "ို", "ǒ", "ǿ", "º", "ო", "ओ", "o", + "ö"}, + {"п", "π", "ပ", "პ", "پ", "p"}, + {"ყ", "q"}, + {"ŕ", "ř", "ŗ", "р", "ρ", "ر", "რ", "r"}, + {"ś", "š", "ş", "с", "σ", "ș", "ς", "س", "ص", "စ", + "ſ", "ს", "s"}, + {"ť", "ţ", "т", "τ", "ț", "ت", "ط", "ဋ", "တ", "ŧ", + "თ", "ტ", "t"}, + {"ú", "ù", "ủ", "ũ", "ụ", "ư", "ứ", "ừ", "ử", "ữ", + "ự", "û", "ū", "ů", "ű", "ŭ", "ų", "µ", "у", "ဉ", + "ု", "ူ", "ǔ", "ǖ", "ǘ", "ǚ", "ǜ", "უ", "उ", "u", + "ў", "ü"}, + {"в", "ვ", "ϐ", "v"}, + {"ŵ", "ω", "ώ", "ဝ", "ွ", "w"}, + {"χ", "ξ", "x"}, + {"ý", "ỳ", "ỷ", "ỹ", "ỵ", "ÿ", "ŷ", "й", "ы", "υ", + "ϋ", "ύ", "ΰ", "ي", "ယ", "y"}, + {"ź", "ž", "ż", "з", "ζ", "ز", "ဇ", "ზ", "z"}, + {"ع", "आ", "آ"}, + {"æ", "ǽ"}, + {"ऐ"}, + {"ч", "ჩ", "ჭ", "چ"}, + {"ђ", "đ"}, + {"џ", "ძ"}, + {"ऍ"}, + {"غ", "ღ"}, + {"ई"}, + {"х", "خ", "ხ"}, + {"ij"}, + {"љ"}, + {"њ"}, + {"œ", "ؤ"}, + {"ऑ"}, + {"ऒ"}, + {"ψ"}, + {"ш", "შ", "ش"}, + {"щ"}, + {"ß"}, + {"ŝ"}, + {"þ", "ϑ", "ث", "ذ", "ظ"}, + {"ц", "ც", "წ"}, + {"ऊ"}, + {"я"}, + {"ю"}, + {"ж", "ჟ", "ژ"}, + {"©"}, + {"Á", "À", "Ả", "Ã", "Ạ", "Ă", "Ắ", "Ằ", "Ẳ", "Ẵ", + "Ặ", "Â", "Ấ", "Ầ", "Ẩ", "Ẫ", "Ậ", "Å", "Ā", "Ą", + "Α", "Ά", "Ἀ", "Ἁ", "Ἂ", "Ἃ", "Ἄ", "Ἅ", "Ἆ", "Ἇ", + "ᾈ", "ᾉ", "ᾊ", "ᾋ", "ᾌ", "ᾍ", "ᾎ", "ᾏ", "Ᾰ", "Ᾱ", + "Ὰ", "Ά", "ᾼ", "А", "Ǻ", "Ǎ", "A", "Ä"}, + {"Б", "Β", "ब", "B"}, + {"Ç", "Ć", "Č", "Ĉ", "Ċ", "C"}, + {"Ď", "Ð", "Đ", "Ɖ", "Ɗ", "Ƌ", "ᴅ", "ᴆ", "Д", "Δ", + "D"}, + {"É", "È", "Ẻ", "Ẽ", "Ẹ", "Ê", "Ế", "Ề", "Ể", "Ễ", + "Ệ", "Ë", "Ē", "Ę", "Ě", "Ĕ", "Ė", "Ε", "Έ", "Ἐ", + "Ἑ", "Ἒ", "Ἓ", "Ἔ", "Ἕ", "Έ", "Ὲ", "Е", "Ё", "Э", + "Є", "Ə", "E"}, + {"Ф", "Φ", "F"}, + {"Ğ", "Ġ", "Ģ", "Г", "Ґ", "Γ", "G"}, + {"Η", "Ή", "Ħ", "H"}, + {"Í", "Ì", "Ỉ", "Ĩ", "Ị", "Î", "Ï", "Ī", "Ĭ", "Į", + "İ", "Ι", "Ί", "Ϊ", "Ἰ", "Ἱ", "Ἳ", "Ἴ", "Ἵ", "Ἶ", + "Ἷ", "Ῐ", "Ῑ", "Ὶ", "Ί", "И", "І", "Ї", "Ǐ", "ϒ", + "I"}, + {"J"}, + {"К", "Κ", "K"}, + {"Ĺ", "Ł", "Л", "Λ", "Ļ", "Ľ", "Ŀ", "ल", "L"}, + {"М", "Μ", "M"}, + {"Ń", "Ñ", "Ň", "Ņ", "Ŋ", "Н", "Ν", "N"}, + {"Ó", "Ò", "Ỏ", "Õ", "Ọ", "Ô", "Ố", "Ồ", "Ổ", "Ỗ", + "Ộ", "Ơ", "Ớ", "Ờ", "Ở", "Ỡ", "Ợ", "Ø", "Ō", "Ő", + "Ŏ", "Ο", "Ό", "Ὀ", "Ὁ", "Ὂ", "Ὃ", "Ὄ", "Ὅ", "Ὸ", + "Ό", "О", "Θ", "Ө", "Ǒ", "Ǿ", "O", "Ö"}, + {"П", "Π", "P"}, + {"Q"}, + {"Ř", "Ŕ", "Р", "Ρ", "Ŗ", "R"}, + {"Ş", "Ŝ", "Ș", "Š", "Ś", "С", "Σ", "S"}, + {"Ť", "Ţ", "Ŧ", "Ț", "Т", "Τ", "T"}, + {"Ú", "Ù", "Ủ", "Ũ", "Ụ", "Ư", "Ứ", "Ừ", "Ử", "Ữ", + "Ự", "Û", "Ū", "Ů", "Ű", "Ŭ", "Ų", "У", "Ǔ", "Ǖ", + "Ǘ", "Ǚ", "Ǜ", "U", "Ў", "Ü"}, + {"В", "V"}, + {"Ω", "Ώ", "Ŵ", "W"}, + {"Χ", "Ξ", "X"}, + {"Ý", "Ỳ", "Ỷ", "Ỹ", "Ỵ", "Ÿ", "Ῠ", "Ῡ", "Ὺ", "Ύ", + "Ы", "Й", "Υ", "Ϋ", "Ŷ", "Y"}, + {"Ź", "Ž", "Ż", "З", "Ζ", "Z"}, + {"Æ", "Ǽ"}, + {"Ч"}, + {"Ђ"}, + {"Џ"}, + {"Ĝ"}, + {"Ĥ"}, + {"IJ"}, + {"Ĵ"}, + {"Х"}, + {"Љ"}, + {"Њ"}, + {"Œ"}, + {"Ψ"}, + {"Ш"}, + {"Щ"}, + {"ẞ"}, + {"Þ"}, + {"Ц"}, + {"Я"}, + {"Yu"}, + {"Ж"}, + {"\xC2\xA0", "\xE2\x80\x80", "\xE2\x80\x81", + "\xE2\x80\x82", "\xE2\x80\x83", "\xE2\x80\x84", + "\xE2\x80\x85", "\xE2\x80\x86", "\xE2\x80\x87", + "\xE2\x80\x88", "\xE2\x80\x89", "\xE2\x80\x8A", + "\xE2\x80\xAF", "\xE2\x81\x9F", "\xE3\x80\x80", + "\xEF\xBE\xA0"}, + }; + + char str[STRINNGY_ROW_COUNT][5] = { + "0", "1", "2","3","4", "5", "6", "7", "8", "9", + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", + "aa", "ae", "ai", "ch", "dj", "dz", "ei", "gh", "ii", "kh", "ij", "lj", "nj", "oe", "oi", "oii", "ps", "sh", "shch", "ss", "sx", "th", "ts", "uu", "ya", "yu", "zh", "(c)", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "AE", "Ch", "Dj", "Dz", "Gx", "Hx", "Ij", "Jx", "Kh", "Lj", "Nj", "Oe", "Ps", "Sh", "Shch", "Ss", "Th", "Ts", "Ya", "Yu", "Zh", " ", + }; + + for (size_t i = 0; i < STRINNGY_ROW_COUNT; i++) + { + zval tmp; + array_init(&tmp); + + for (size_t j = 0; j < STRINNGY_COLUM_COUNT; j++) + { + if (strlen(str_arr[i][j]) == 0) { + break; + } + + add_next_index_string(&tmp, str_arr[i][j]); + } + + add_assoc_zval(&ret, str[i], &tmp); + } + + + RETURN_ZVAL(&ret, 0, 0); +} + +PHP_METHOD(Stringy, langSpecificCharsArray) +{ + zval *language; + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(language) + ZEND_PARSE_PARAMETERS_END(); + + if (language == NULL) { + language = emalloc(sizeof(zval)); + ZVAL_STRING(language, "en"); + } + + zval reg; + ZVAL_STRING(®, "/[-_]/"); + zval func, args[] = { + reg, + *language, + }; + ZVAL_STRING(&func, "preg_split"); + call_user_function(NULL, NULL, &func, return_value, 2, args); + + zval args_strtolower[] = { + *zend_hash_index_find(Z_ARRVAL_P(return_value), (zend_long) 0), + }; + ZVAL_STRING(&func, "strtolower"); + call_user_function(NULL, NULL, &func, return_value, 1, args_strtolower); + + zval languageSpecific; + array_init(&languageSpecific); + + zval tb_0; + array_init(&tb_0); + char* th_0_arr[6] = {"ä", "ö", "ü", "Ä", "Ö", "Ü"}; + for(int i = 0; i < 6; i++) { + add_next_index_string(&tb_0, th_0_arr[i]); + } + + zval tb_1; + array_init(&tb_1); + char* th_1_arr[6] = {"ae", "oe", "ue", "AE", "OE", "UE"}; + for(int i = 0; i < 6; i++) { + add_next_index_string(&tb_1, th_1_arr[i]); + } + + zval val_0; + array_init(&val_0); + add_next_index_zval(&val_0, &tb_0); + add_next_index_zval(&val_0, &tb_1); + + add_assoc_zval(&languageSpecific, "de", &val_0); + + zval tb_1_0; + array_init(&tb_1_0); + char* th_1_0_arr[8] = {"х", "Х", "щ", "Щ", "ъ", "Ъ", "ь", "Ь"}; + for(int i = 0; i < 8; i++) { + add_next_index_string(&tb_1_0, th_1_0_arr[i]); + } + + zval tb_1_1; + array_init(&tb_1_1); + char* th_1_1_arr[8] = {"h", "H", "sht", "SHT", "a", "А", "y", "Y"}; + for(int i = 0; i < 8; i++) { + add_next_index_string(&tb_1_1, th_1_1_arr[i]); + } + + zval val_1; + array_init(&val_1); + add_next_index_zval(&val_1, &tb_0); + add_next_index_zval(&val_1, &tb_1); + + add_assoc_zval(&languageSpecific, "bg", &val_1); + + zval ret; + zend_string *key = Z_STR_P(return_value); + if (zend_hash_exists(Z_ARRVAL(languageSpecific), key)) { + ret = *zend_hash_find(Z_ARR(languageSpecific), key); + } else { + array_init(&ret); + } + + RETURN_ZVAL(&ret, 0, 1); +} +ZEND_BEGIN_ARG_INFO(arginfo_langSpecificCharsArray, 0) + ZEND_ARG_INFO(0, language) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, toSpaces) +{ + zval * tabLength; + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(tabLength) + ZEND_PARSE_PARAMETERS_END(); + + zval rv; + zval * str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + zval * encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 1, &rv); + + zval tmp_str; + ZVAL_STRING(&tmp_str, " "); + + convert_to_long(tabLength); + zval func, args[] = { + tmp_str, + *tabLength, + }; + ZVAL_STRING(&func, "str_repeat"); + call_user_function(NULL, NULL, &func, return_value, 2, args); + + ZVAL_STRING(&tmp_str, "\t"); + zval args_str_replace[] = { + tmp_str, + *return_value, + *str, + }; + ZVAL_STRING(&func, "str_replace"); + call_user_function(NULL, NULL, &func, return_value, 3, args_str_replace); + + zval instance; + object_init_ex(&instance, stringy_ce); + + zval args_construct[] = { + *return_value, + *encoding, + }; + ZVAL_STRING(&func, "__construct"); + call_user_function(NULL, &instance, &func, return_value, 2, args_construct); + + RETURN_ZVAL(&instance, 0, 1); +} +ZEND_BEGIN_ARG_INFO(arginfo_toSpaces, 0) + ZEND_ARG_INFO(0, tabLength) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, underscored) +{ + zval delimit; + ZVAL_STRING(&delimit, "_"); + zval func, args[] = { + delimit, + }; + ZVAL_STRING(&func, "delimit"); + call_user_function(NULL, getThis(), &func, return_value, 1, args); +} + +PHP_METHOD(Stringy, toUpperCase) +{ + zval rv; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 1, &rv); + + + zval func, args[] = { + *str, + *encoding, + }; + ZVAL_STRING(&func, "mb_strtoupper"); + call_user_function(NULL, NULL, &func, return_value, 2, args); + + zval instance; + object_init_ex(&instance, stringy_ce); + + zval args_construct[] = { + *return_value, + *encoding, + }; + ZVAL_STRING(&func, "__construct"); + call_user_function(NULL, &instance, &func, return_value, 2, args_construct); + + RETURN_ZVAL(&instance, 0, 1); +} + +PHP_METHOD(Stringy, truncate) +{ + zval *len, *substring; + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(len); + Z_PARAM_ZVAL(substring); + ZEND_PARSE_PARAMETERS_END(); + + zval rv; + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + + zval object; + object_init_ex(&object, stringy_ce); + zval method, args[] = { + *str, + *substring, + }; + ZVAL_STRING(&method, "__construct"); + call_user_function(NULL, &object, &method, return_value, 2, args); + + ZVAL_STRING(&method, "length"); + call_user_function(NULL, getThis(), &method, return_value, 0, NULL); + + if (Z_LVAL_P(len) >= Z_LVAL_P(return_value)) { + RETURN_ZVAL(&object, 0, 0); + } + + zval *strNew = zend_read_property(stringy_ce, &object, ZEND_STRL("str"), 0, &rv); + zval args_mb_string[] = { + *substring, + *encoding, + }; + ZVAL_STRING(&method, "mb_strlen"); + call_user_function(NULL, NULL, &method, return_value, 1, args_mb_string); + + size_t substringLength = Z_LVAL_P(return_value); + size_t length = Z_LVAL_P(len); + length = length - substringLength; + + zval zero, zvalLen; + ZVAL_LONG(&zero, 0); + ZVAL_LONG(&zvalLen, length); + zval args_mb_substr[] = { + *strNew, + zero, + zvalLen, + *encoding, + }; + ZVAL_STRING(&method, "mb_substr"); + call_user_function(NULL, NULL, &method, return_value, 4, args_mb_substr); + + zval newVal; + concat_function(&newVal, return_value, substring); + zend_update_property(stringy_ce, &object, ZEND_STRL("str"), &newVal); + + RETURN_ZVAL(&object, 0, 1); +} +ZEND_BEGIN_ARG_INFO(arginfo_truncate, 0) + ZEND_ARG_INFO(0, length) + ZEND_ARG_INFO(0, substring) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, toTitleCase) +{ + zval *str, *encoding, rv; + str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + // ZVAL_STRING(&const_mb_case_title, "MB_CASE_TITLE"); + zval *const_mb_case_title = zend_get_constant_str(ZEND_STRL("MB_CASE_TITLE")); + + zval method, args[] = { + *str, + *const_mb_case_title, + *encoding, + }; + ZVAL_STRING(&method, "mb_convert_case"); + call_user_function(NULL, NULL, &method, return_value, 3, args); + + zval obj; + object_init_ex(&obj, stringy_ce); + zval args_construct[] = { + *return_value, + *encoding, + }; + ZVAL_STRING(&method, "__construct"); + call_user_function(NULL, &obj, &method, return_value, 2, args_construct); + + RETURN_ZVAL(&obj, 0, 1); +} + +PHP_METHOD(Stringy, toTabs) +{ + zval *tabLength; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(tabLength) + ZEND_PARSE_PARAMETERS_END(); + + if (tabLength == NULL) { + tabLength = emalloc(sizeof(zval)); + ZVAL_LONG(tabLength, 4); + } + + zval empty; + ZVAL_STRING(&empty, " "); + + zval func, args[] = { + empty, + *tabLength + }; + ZVAL_STRING(&func, "str_repeat"); + call_user_function(NULL, NULL, &func, return_value, 2, args); + + zval *str, *encoding, rv; + str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval tab; + ZVAL_STRING(&tab, "\t"); + + zval args_str_replace[] = { + *return_value, + tab, + *str + }; + ZVAL_STRING(&func, "str_replace"); + call_user_function(NULL, NULL, &func, return_value, 3, args_str_replace); + + zval obj; + object_init_ex(&obj, stringy_ce); + zval args_construct[] = { + *return_value, + *encoding, + }; + ZVAL_STRING(&func, "__construct"); + call_user_function(NULL, &obj, &func, return_value, 2, args_construct); + + RETURN_ZVAL(&obj, 0, 1); +} + +PHP_METHOD(Stringy, toLowerCase) +{ + zval *str, *encoding, rv; + str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval func = {}; + ZVAL_STRING(&func, "mb_strtolower"); + zval args_str_to_lower[] = { + *str, + }; + call_user_function(NULL, NULL, &func, return_value, 1, args_str_to_lower); + + zval obj; + object_init_ex(&obj, stringy_ce); + zval args_construct[] = { + *return_value, + *encoding, + }; + ZVAL_STRING(&func, "__construct"); + call_user_function(NULL, &obj, &func, return_value, 2, args_construct); + + RETURN_ZVAL(&obj, 0, 1); +} + +PHP_METHOD(Stringy, toBoolean) +{ + zval map, T, F; + ZVAL_TRUE(&T); + ZVAL_FALSE(&F); + + array_init(&map); + char* trueList[] = { + "true", + "1", + "on", + "yes" + }; + for (size_t i = 0; i < 4; i++) + { + add_assoc_zval(&map, trueList[i], &T); + } + + char* falseList[] = { + "false", + "0", + "off", + "no" + }; + for (size_t i = 0; i < 4; i++) + { + add_assoc_zval(&map, falseList[i], &F); + } + + zval func, rv; + ZVAL_STRING(&func, "toLowerCase"); + call_user_function(NULL, getThis(), &func, return_value, 0, NULL); + + zval *str = zend_read_property(stringy_ce, return_value, ZEND_STRL("str"), 0, &rv); + + zend_string *key = Z_STR(*str); + HashTable *ht = Z_ARRVAL(map); + if (zend_symtable_exists_ind(ht, key)){ + RETURN_ZVAL(zend_symtable_find(ht, key), 0, 0) + } + + if (is_numeric_string(Z_STRVAL_P(str), Z_STRLEN_P(str), NULL, NULL, 0) == IS_LONG) { + convert_to_long(str); + RETURN_BOOL(Z_LVAL_P(str) > 0); + } + + zval regex, empty; + ZVAL_STRING(®ex, "[[:space:]]"); + ZVAL_STRING(&func, "regexReplace"); + ZVAL_EMPTY_STRING(&empty); + zval args_regex[] = { + regex, + empty, + }; + call_user_function(NULL, getThis(), &func, return_value, 2, args_regex); + + str = zend_read_property(stringy_ce, return_value, ZEND_STRL("str"), 0, &rv); + + convert_to_boolean(str); + RETURN_ZVAL(str, 0, 0); +} + +static zval __ignore; +static zval __encoding; +static void titleize_handler(INTERNAL_FUNCTION_PARAMETERS) +{ + zval *arr; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(arr) + ZEND_PARSE_PARAMETERS_END(); + + bool flag = false; + zval *first = zend_hash_index_find(Z_ARRVAL_P(arr), 0); + + zval func, args[] = { + *first, + __ignore, + }; + ZVAL_STRING(&func, "in_array"); + call_user_function(NULL, NULL, &func, return_value, 2, args); + convert_to_boolean(return_value); + + if (IS_NULL != Z_TYPE(__ignore) && Z_TYPE_P(return_value)==IS_TRUE) { + RETURN_ZVAL(first, 1, 1); + } + + zval obj; + convert_to_string(return_value); + object_init_ex(&obj, stringy_ce); + zval args_construct[] = { + *first, + __encoding, + }; + ZVAL_STRING(&func, "__construct"); + call_user_function(NULL, &obj, &func, return_value, 2, args_construct); + + ZVAL_STRING(&func, "toLowerCase"); + call_user_function(NULL, &obj, &func, return_value, 0, NULL); + + ZVAL_STRING(&func, "upperCaseFirst"); + call_user_function(NULL, return_value, &func, return_value, 0, NULL); + + convert_to_string(return_value); +} +PHP_METHOD(Stringy, titleize) { + zval *ignore; + ignore = emalloc(sizeof(zval)); + ZVAL_NULL(ignore); + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(ignore) + ZEND_PARSE_PARAMETERS_END(); + + zval rv; + zval *str = zend_read_property(NULL, getThis(), ZEND_STRL("str"), 0, &rv); + zval *encoding = zend_read_property(NULL, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval func; + ZVAL_STRING(&func, "trim"); + call_user_function(NULL, getThis(), &func, return_value, 0, NULL); + + zval obj; + convert_to_string(return_value); + object_init_ex(&obj, stringy_ce); + zval args_construct[] = { + *return_value, + *encoding, + }; + ZVAL_STRING(&func, "__construct"); + call_user_function(NULL, &obj, &func, return_value, 2, args_construct); + + str = zend_read_property(NULL, &obj, ZEND_STRL("str"), 0, &rv); + + + zend_internal_arg_info zai[] = { + ZEND_ARG_ARRAY_INFO(0, "matches", 0) + }; + + zend_string *f = zend_string_init(ZEND_STRL("callback"), 0); + + zend_function zendFunction; + zendFunction.internal_function.function_name = f; + zendFunction.internal_function.handler = titleize_handler; + zendFunction.internal_function.type = ZEND_INTERNAL_FUNCTION; + zendFunction.internal_function.fn_flags = ZEND_ACC_CLOSURE; + zendFunction.internal_function.arg_info = zai; + zendFunction.internal_function.required_num_args =1; + zendFunction.internal_function.num_args = 1; + + + zval callback; + zend_create_closure(&callback, &zendFunction, NULL, NULL, NULL); + // 这个不知道怎么模拟use + if (Z_TYPE_P(ignore) == IS_NULL) { + array_init(ignore); + } + + __ignore = *ignore; + __encoding = *encoding; + + ZVAL_STRING(&func, "preg_replace_callback"); + zval reg; + ZVAL_STRING(®, "/([\\S]+)/u"); + zval args_preg_replace_callback[] = { + reg, + callback, + *str, + }; + call_user_function(NULL, NULL, &func, return_value, 3, args_preg_replace_callback); + + zend_update_property(NULL, &obj, ZEND_STRL("str"), return_value); + + RETURN_ZVAL(&obj, 1, 1); +} +ZEND_BEGIN_ARG_INFO(arginfo_titleize, 1) + ZEND_ARG_INFO(0, ignore) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, reverse) { + zval func = {}; + ZVAL_STRING(&func, "length"); + call_user_function(NULL, getThis(), &func, return_value, 0, NULL); + + long len = Z_LVAL_P(return_value); + + + if (len == 0) { + RETURN_EMPTY_STRING(); + } + + zval reversed = {}; + + zval rv = {}; + zval *str = zend_read_property(stringy_ce, getThis(), ZEND_STRL("str"), 0, &rv); + zval *encoding = zend_read_property(stringy_ce, getThis(), ZEND_STRL("encoding"), 0, &rv); + + for (int i = len-1; i >=0; i--) + { + zval zval_i; + ZVAL_LONG(&zval_i, i); + + zval zval_1; + ZVAL_LONG(&zval_1, 1); + + ZVAL_STRING(&func, "mb_substr"); + zval args[] = { + *str, + zval_i, + zval_1 , + *encoding + }; + call_user_function(NULL, NULL, &func, return_value, 4, args); + + concat_function(&reversed, &reversed, return_value); + } + + zval obj; + object_init_ex(&obj, stringy_ce); + zval args_construct[] = { + reversed, + *encoding, + }; + ZVAL_STRING(&func, "__construct"); + call_user_function(NULL, &obj, &func, return_value, 2, args_construct); + + RETURN_ZVAL(&obj, 0, 1); +} + +PHP_METHOD(Stringy, safeTruncate) { + zval *length; + zval *substring; + + substring = emalloc(sizeof(zval)); + + ZVAL_STRING(substring, ""); + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(length) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(substring) + ZEND_PARSE_PARAMETERS_END(); + + zval rv; + zval *str = zend_read_property(NULL, getThis(), ZEND_STRL("str"), 0, &rv); + zval *encoding = zend_read_property(NULL, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval obj; + object_init_ex(&obj, stringy_ce); + zval args_construct[] = { + *str, + *encoding, + }; + zval func; + ZVAL_STRING(&func, "__construct"); + call_user_function(NULL, &obj, &func, return_value, 2, args_construct); + + ZVAL_STRING(&func, "length"); + call_user_function(NULL, &obj, &func, return_value, 0, NULL); + + int length_o = Z_LVAL_P(length); + int length_n = Z_LVAL_P(return_value); + if (length_o >= length_n) { + RETURN_ZVAL(&obj, 0, 0); + } + + ZVAL_STRING(&func, "mb_strlen"); + zval args_mb_strlen[] = { + *substring, + *encoding, + }; + call_user_function(NULL, NULL, &func, return_value, 2, args_mb_strlen); + length_o = length_o - Z_LVAL_P(return_value); + + ZVAL_STRING(&func, "mb_substr"); + zval zval_0; + ZVAL_BOOL(&zval_0, 0); + zval zval_length; + ZVAL_LONG(&zval_length, length_o); + zval args_mb_substr[] = { + *str, + zval_0, + zval_length, + *encoding, + }; + zval truncated; + call_user_function(NULL, NULL, &func, &truncated, 4, args_mb_substr); + + ZVAL_STRING(&func, "mb_strpos"); + zval estr; + ZVAL_STRING(&estr, " "); + ZVAL_LONG(&zval_length, length_o - 1); + zval args_mb_strpos[] = { + *str, + estr, + zval_length, + *encoding, + }; + call_user_function(NULL, NULL, &func, return_value, 4, args_mb_strpos); + + if (Z_LVAL_P(return_value) != length_o) { + ZVAL_STRING(&func, "mb_strrpos"); + zval length_1; + ZVAL_LONG(&length_1, length_o-1); + zval args_mb_strrpos[] = { + truncated, + estr, + zval_0, + *encoding, + }; + call_user_function(NULL, NULL, &func, return_value, 4, args_mb_strrpos); + if (Z_TYPE_P(return_value) != IS_FALSE) { + ZVAL_STRING(&func, "mb_substr"); + zval zval_0; + ZVAL_BOOL(&zval_0, 0); + zval args_mb_substr1[] = { + truncated, + zval_0, + *return_value, + *encoding, + }; + call_user_function(NULL, NULL, &func, &truncated, 4, args_mb_substr1); + } + } + + concat_function(return_value, &truncated, substring); + + zend_string *str_name = zend_string_init(ZEND_STRL("str"), 0); + zend_update_property_ex(NULL, &obj, str_name, return_value); + + RETURN_ZVAL(&obj, 1, 0); + +} +ZEND_BEGIN_ARG_INFO(arginfo_safeTruncate, 0) + ZEND_ARG_INFO(0, length) + ZEND_ARG_INFO(0, substring) +ZEND_END_ARG_INFO(); + +PHP_METHOD(Stringy, shuffle) +{ + zval zval_0; + ZVAL_LONG(&zval_0, 0); + + zval func; + ZVAL_STRING(&func, "length"); + call_user_function(NULL, getThis(), &func, return_value, 0, NULL); + + long l = Z_LVAL_P(return_value); + + zval indexes; + zval zval_len; + ZVAL_LONG(&zval_len, l - 1); + zval args[] = { + zval_0, + zval_len + }; + ZVAL_STRING(&func, "range"); + call_user_function(NULL, NULL, &func, &indexes, 2, args); + + zval ref; + ZVAL_NEW_REF(&ref, &indexes); + + ZVAL_STRING(&func, "shuffle"); + zval args_shuffle[] = { + ref, + }; + call_user_function(NULL, NULL, &func, return_value, 1, args_shuffle); + + zval shuffledStr; + + zval rv; + zval *str = zend_read_property(NULL, getThis(), ZEND_STRL("str"), 0, &rv); + zval *encoding = zend_read_property(NULL, getThis(), ZEND_STRL("encoding"), 0, &rv); + + zval *value; + zend_string *key; + zval x; + ulong idx; + ZEND_HASH_REVERSE_FOREACH_KEY_VAL_IND(Z_ARRVAL(indexes), idx, key, value) + zval zval_1; + ZVAL_LONG(&zval_1, 1); + + zval args_mb_substr[] = { + *str, + *value, + zval_1, + *encoding, + }; + ZVAL_STRING(&func, "mb_substr"); + call_user_function(NULL, NULL, &func, return_value, 4, args_mb_substr); + + concat_function(&shuffledStr, &shuffledStr, return_value); + ZEND_HASH_FOREACH_END(); + + zval obj; + object_init_ex(&obj, stringy_ce); + zval args_construct[] = { + shuffledStr, + *encoding, + }; + ZVAL_STRING(&func, "__construct"); + call_user_function(NULL, &obj, &func, return_value, 2, args_construct); + + + RETURN_ZVAL(&obj, 0, 0); +} + +static zend_function_entry methods[] = { + PHP_ME(Stringy, __construct, arginfo___construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(Stringy, __toString, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, getEncoding, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, create, arginfo_create, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Stringy, count, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, length, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, indexOfLast, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, indexOf, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, getIterator, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, chars, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, at, arginfo_at, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, substr, arginfo_substr, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, offsetExists, arginfo_offsetExists, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, offsetGet, arginfo_offsetGet, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, offsetSet, arginfo_offsetSet, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, offsetUnset, arginfo_offsetUnset, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, collapseWhiteSpace, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, regexReplace, arginfo_regexReplace, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, regexEncoding, arginfo_regexEncoding, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, eregReplace, arginfo_eregReplace, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, swapCase, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, upperCaseFirst, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, lowerCaseFirst, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, append, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, prepend, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, lines, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, split, arginfo_split, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, camelize, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, trim, arginfo_trim, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, trimLeft, arginfo_trimLeft, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, trimRight, arginfo_trimRight, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, between, arginfo_between, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, contains, arginfo_contains, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, containsAll, arginfo_containsAll, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, containsAny, arginfo_containsAny, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, countSubstr, arginfo_countSubstr, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, delimit, arginfo_delimit, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, dasherize, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, endsWith, arginfo_endsWith, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, startsWith, arginfo_startsWith, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, endsWithAny, arginfo_endsWithAny, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, ensureLeft, arginfo_ensureLeft, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, ensureRight, arginfo_ensureRight, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, first, arginfo_first, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, last, arginfo_last, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, isAlpha, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, isAlphanumeric, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, isHexadecimal, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, isLowerCase, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, isUpperCase, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, matchesPattern, arginfo_matchesPattern, ZEND_ACC_PROTECTED) + PHP_ME(Stringy, isBlank, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, hasLowerCase, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, hasUpperCase, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, htmlEncode, arginfo_htmlEncode, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, htmlDecode, arginfo_htmlDecode, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, removeLeft, arginfo_removeLeft, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, removeRight, arginfo_removeRight, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, humanize, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, insert, arginfo_insert, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, isJson, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, isSerialized, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, isBase64, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, longestCommonPrefix, arginfo_longestCommonPrefix, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, longestCommonSuffix, arginfo_longestCommonSuffix, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, longestCommonSubstring, arginfo_longestCommonSubstring, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, pad, arginfo_pad, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, padLeft, arginfo_padLeft, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, padRight, arginfo_padRight, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, padBoth, arginfo_padBoth, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, applyPadding, arginfo_applyPadding, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, repeat, arginfo_repeat, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, replace, arginfo_replace, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, toAscii, arginfo_toAscii, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, charsArray, NULL, ZEND_ACC_PROTECTED) + PHP_ME(Stringy, langSpecificCharsArray, arginfo_langSpecificCharsArray, ZEND_ACC_PROTECTED) + PHP_ME(Stringy, toSpaces, arginfo_toSpaces, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, underscored, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, toUpperCase, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, truncate, arginfo_truncate, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, toTitleCase, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, toTabs, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, toLowerCase, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, toBoolean, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, titleize, arginfo_titleize, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, reverse, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, safeTruncate, arginfo_safeTruncate, ZEND_ACC_PUBLIC) + PHP_ME(Stringy, shuffle, NULL, ZEND_ACC_PUBLIC) + PHP_FE_END +}; + +void php_startup_register_stringy() +{ + zend_class_entry ce; + + INIT_CLASS_ENTRY(ce, PHP_STARTUP_STRINGY_NS(Stringy), methods); + + stringy_ce = zend_register_internal_class(&ce); + + zend_declare_property_string(stringy_ce, ZEND_STRL("str"), "", ZEND_ACC_PROTECTED); + zend_declare_property_string(stringy_ce, ZEND_STRL("str"), "", ZEND_ACC_PROTECTED); + + zend_class_implements( + stringy_ce, + 3, + spl_ce_Countable, + spl_ce_Aggregate, + spl_ce_ArrayAccess + ); +} diff --git a/src/ext/stringy/stringy.h b/src/ext/stringy/stringy.h new file mode 100644 index 0000000..8302d65 --- /dev/null +++ b/src/ext/stringy/stringy.h @@ -0,0 +1,26 @@ +#ifndef STARTUP_STRINGY_STRINGY_H +#define STARTUP_STRINGY_STRINGY_H + +#include "php.h" +#include "../common.h" +#include "../functions.h" +#include "ext/standard/php_standard.h" +#include "Zend/zend_exceptions.h" +#include "ext/spl/spl_exceptions.h" +#include "ext/spl/spl_iterators.h" +#include "ext/mbstring/mbstring.h" +#include "zend_interfaces.h" +#include "zend_closures.h" +#include "ext/spl/spl_array.h" + +#define PHP_STARTUP_STRINGY_NS(cls) PHP_STARTUP_NS_NAME "Stringy\\" #cls + +#define STRINNGY_ROW_COUNT 112 + +#define STRINNGY_COLUM_COUNT 60 + +extern zend_class_entry *stringy_ce; + +void php_startup_register_stringy(); + +#endif diff --git a/src/ext/test.class.c b/src/ext/test.class.c index 404b750..860a9b0 100644 --- a/src/ext/test.class.c +++ b/src/ext/test.class.c @@ -4,92 +4,92 @@ zend_class_entry *test_ce; PHP_METHOD(Test, helloworld) { - if (zend_parse_parameters_none() == FAILURE ) { + if (zend_parse_parameters_none() == FAILURE ) { return; } - - php_printf("hello world!"); - RETVAL_NULL(); + + php_printf("hello world!"); + RETVAL_NULL(); } PHP_METHOD(Test, echoHelloWorld) { - if (zend_parse_parameters_none() == FAILURE ) { + if (zend_parse_parameters_none() == FAILURE ) { return; } - php_printf("hello world!!"); + php_printf("hello world!!"); - RETVAL_NULL(); + RETURN_NULL(); } PHP_METHOD(Test, __call) { zval *params; char *method; - size_t method_len; - zend_string *strg; + size_t method_len; + zend_string *strg; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &method, &method_len, ¶ms) == FAILURE) { return; } - zend_string *delim = zend_string_init("-", sizeof("-"), 0); + zend_string *delim = zend_string_init("-", sizeof("-"), 0); - zval *tmp; - zval result = {}; - zval delimPtr = {}; - - ZVAL_STRINGL(&delimPtr, "-", 1); - ZVAL_EMPTY_STRING(&result); + zval *tmp; + zval result = {}; + zval delimPtr = {}; + + ZVAL_STRINGL(&delimPtr, "-", 1); + ZVAL_EMPTY_STRING(&result); - // 初学者不知道这里有什么其他实现方式。这里以能用为准 + // 初学者不知道这里有什么其他实现方式。这里以能用为准 - uint32_t count = zend_array_count(params->value.arr); - uint32_t index = 0; + uint32_t count = zend_array_count(params->value.arr); + uint32_t index = 0; - if (count > 0) { - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(params), tmp) { - concat_function(&result, &result, tmp); + if (count > 0) { + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(params), tmp) { + concat_function(&result, &result, tmp); - if (index == count-1) { - break; - } + if (index == count-1) { + break; + } - concat_function(&result, &result, &delimPtr); - index ++; - } ZEND_HASH_FOREACH_END(); - } + concat_function(&result, &result, &delimPtr); + index ++; + } ZEND_HASH_FOREACH_END(); + } - strg = strpprintf(0, "method:%s,count:%d,args:%s", method, count, Z_STRVAL_P(&result)); + strg = strpprintf(0, "method:%s,count:%d,args:%s", method, count, Z_STRVAL_P(&result)); - RETURN_STR(strg); + RETURN_STR(strg); } ZEND_BEGIN_ARG_INFO_EX(arginfo_call, 0, 0, 2) - ZEND_ARG_INFO(0, method) - ZEND_ARG_INFO(0, params) + ZEND_ARG_INFO(0, method) + ZEND_ARG_INFO(0, params) ZEND_END_ARG_INFO() static zend_function_entry methods[] = { - PHP_ME(Test, helloworld, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_ME(Test, echoHelloWorld, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Test, __call, arginfo_call, ZEND_ACC_PUBLIC) - PHP_FE_END + PHP_ME(Test, helloworld, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + PHP_ME(Test, echoHelloWorld, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Test, __call, arginfo_call, ZEND_ACC_PUBLIC) + PHP_FE_END }; -void php_start_register_test() +void php_startup_register_test() { - zend_class_entry ce; + zend_class_entry ce; - INIT_CLASS_ENTRY(ce, PHP_STARTUP_NS(Test), methods); + INIT_CLASS_ENTRY(ce, PHP_STARTUP_TEST_NS(Test), methods); - test_ce = zend_register_internal_class(&ce); + test_ce = zend_register_internal_class(&ce); - zend_declare_property_string(test_ce, "publicProperty", strlen("publicProperty"), "hello world +property", ZEND_ACC_PUBLIC); + zend_declare_property_string(test_ce, "publicProperty", strlen("publicProperty"), "hello world +property", ZEND_ACC_PUBLIC); - zend_declare_property_string(test_ce, "publicPropertyStatic", strlen("publicPropertyStatic"), "hello world +property +static", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC); + zend_declare_property_string(test_ce, "publicPropertyStatic", strlen("publicPropertyStatic"), "hello world +property +static", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC); - zend_declare_class_constant_string(test_ce, "PUBLIC_CONST", strlen("PUBLIC_CONST"), "hello world +const"); + zend_declare_class_constant_string(test_ce, "PUBLIC_CONST", strlen("PUBLIC_CONST"), "hello world +const"); } diff --git a/src/ext/test.class.h b/src/ext/test.class.h index 82ebc06..cb6742f 100644 --- a/src/ext/test.class.h +++ b/src/ext/test.class.h @@ -5,8 +5,10 @@ #include "common.h" #include "ext/standard/php_standard.h" +#define PHP_STARTUP_TEST_NS(cls) PHP_STARTUP_NS_NAME #cls + extern zend_class_entry *test_ce; -void php_start_register_test(); +void php_startup_register_test(); #endif diff --git a/startup.c b/startup.c index 1e88961..f67ed38 100644 --- a/startup.c +++ b/startup.c @@ -28,14 +28,14 @@ #include "zend_types.h" #include "php_startup.h" #include "src/ext/test.class.h" +#include "src/ext/stringy/stringy.h" +#include "src/ext/functions.h" +#include "zend_closures.h" /* If you declare any globals in php_startup.h uncomment this: ZEND_DECLARE_MODULE_GLOBALS(startup) */ -zend_class_entry *myclass_ce; - - /* True global resources - no need for thread safety here */ static int le_startup; @@ -58,17 +58,17 @@ PHP_INI_END() Return a string to confirm that the module is compiled in */ PHP_FUNCTION(confirm_startup_compiled) { - char *arg = NULL; - size_t arg_len, len; - zend_string *strg; + char *arg = NULL; + size_t arg_len, len; + zend_string *strg; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg, &arg_len) == FAILURE) { - return; - } + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg, &arg_len) == FAILURE) { + return; + } - strg = strpprintf(0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "startup", arg); + strg = strpprintf(0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "startup", arg); - RETURN_STR(strg); + RETURN_STR(strg); } /* }}} */ /* The previous line is meant for vim and emacs, so it can correctly fold and @@ -82,42 +82,23 @@ PHP_FUNCTION(confirm_startup_compiled) /* Uncomment this function if you have INI entries static void php_startup_init_globals(zend_startup_globals *startup_globals) { - startup_globals->global_value = 0; - startup_globals->global_string = NULL; + startup_globals->global_value = 0; + startup_globals->global_string = NULL; } */ /* }}} */ - -PHP_METHOD(myclass, abab) -{ - php_printf("我是public\n"); -} - -static zend_function_entry myclass_method[] = { - PHP_ME(myclass, abab, NULL, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} -}; - - /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(startup) { - /* If you have INI entries, uncomment these lines - REGISTER_INI_ENTRIES(); - */ - - zend_class_entry ce; - - INIT_CLASS_ENTRY(ce, "myclass", myclass_method); - myclass_ce = zend_register_internal_class(&ce TSRMLS_CC); + /* If you have INI entries, uncomment these lines + REGISTER_INI_ENTRIES(); + */ + php_startup_register_test(); + php_startup_register_stringy(); - zend_declare_property_null(myclass_ce, "pub_var", strlen("pub_var"), ZEND_ACC_PUBLIC TSRMLS_CC); - - php_start_register_test(); - - return SUCCESS; + return SUCCESS; } /* }}} */ @@ -125,10 +106,10 @@ PHP_MINIT_FUNCTION(startup) */ PHP_MSHUTDOWN_FUNCTION(startup) { - /* uncomment this line if you have INI entries - UNREGISTER_INI_ENTRIES(); - */ - return SUCCESS; + /* uncomment this line if you have INI entries + UNREGISTER_INI_ENTRIES(); + */ + return SUCCESS; } /* }}} */ @@ -138,9 +119,9 @@ PHP_MSHUTDOWN_FUNCTION(startup) PHP_RINIT_FUNCTION(startup) { #if defined(COMPILE_DL_STARTUP) && defined(ZTS) - ZEND_TSRMLS_CACHE_UPDATE(); + ZEND_TSRMLS_CACHE_UPDATE(); #endif - return SUCCESS; + return SUCCESS; } /* }}} */ @@ -149,7 +130,7 @@ PHP_RINIT_FUNCTION(startup) */ PHP_RSHUTDOWN_FUNCTION(startup) { - return SUCCESS; + return SUCCESS; } /* }}} */ @@ -157,40 +138,54 @@ PHP_RSHUTDOWN_FUNCTION(startup) */ PHP_MINFO_FUNCTION(startup) { - php_info_print_table_start(); - php_info_print_table_header(2, "startup support", "enabled"); - php_info_print_table_row(2, "version", PHP_STARTUP_VERSION); - php_info_print_table_end(); - - /* Remove comments if you have entries in php.ini - DISPLAY_INI_ENTRIES(); - */ + php_info_print_table_start(); + php_info_print_table_header(2, "startup support", "enabled"); + php_info_print_table_row(2, "version", PHP_STARTUP_VERSION); + php_info_print_table_end(); + + /* Remove comments if you have entries in php.ini + DISPLAY_INI_ENTRIES(); + */ } /* }}} */ +// zval* check_ext_load(char* name) +// { +// zval return_value, z_name; +// ZVAL_STRING(&z_name, name); +// zval func, args[] = { +// z_name, +// }; +// ZVAL_STRING(&func, "extension_loaded"); +// call_user_function(NULL, NULL, &func, &return_value, 1, args); +// // convert_to_boolean(&return_value); +// // return zval_is_true(&return_value); +// return &return_value; +// } + /* {{{ startup_functions[] * * Every user visible function must have an entry in startup_functions[]. */ -const zend_function_entry startup_functions[] = { - PHP_FE(confirm_startup_compiled, NULL) /* For testing, remove later. */ - PHP_FE_END /* Must be the last line in startup_functions[] */ +zend_function_entry startup_functions[] = { + PHP_FE(confirm_startup_compiled, NULL) /* For testing, remove later. */ + PHP_FE_END /* Must be the last line in startup_functions[] */ }; /* }}} */ /* {{{ startup_module_entry */ zend_module_entry startup_module_entry = { - STANDARD_MODULE_HEADER, - "startup", - startup_functions, - PHP_MINIT(startup), - PHP_MSHUTDOWN(startup), - PHP_RINIT(startup), /* Replace with NULL if there's nothing to do at request start */ - PHP_RSHUTDOWN(startup), /* Replace with NULL if there's nothing to do at request end */ - PHP_MINFO(startup), - PHP_STARTUP_VERSION, - STANDARD_MODULE_PROPERTIES + STANDARD_MODULE_HEADER, + "startup", + startup_functions, + PHP_MINIT(startup), + PHP_MSHUTDOWN(startup), + PHP_RINIT(startup), /* Replace with NULL if there's nothing to do at request start */ + PHP_RSHUTDOWN(startup), /* Replace with NULL if there's nothing to do at request end */ + PHP_MINFO(startup), + PHP_STARTUP_VERSION, + STANDARD_MODULE_PROPERTIES }; /* }}} */ diff --git a/startup.php b/startup.php index 55385ef..8371839 100644 --- a/startup.php +++ b/startup.php @@ -3,51 +3,9 @@ $br = (php_sapi_name() == 'cli') ? '' : '
'; $module = 'startup'; -function handleFunc() -{ - global $br, $module; +include __DIR__.'/vendor/autoload.php'; - $function = 'confirm_'.$module.'_compiled'; - if (extension_loaded($module)) { - $str = $function($module); - } else { - $str = "Module $module is not compiled into PHP"; - } - echo "$str\n"; -} +_ns(NS_STRINGY); -function handleClass() -{ - if (class_exists('myclass')) { - echo 'myclass exists'; - } else { - echo 'myclass exists'; - } - $class = new myclass(); - var_dump($class); - $class->abab(); -} - -function checkExt() -{ - global $br, $module; - if (!extension_loaded('startup')) { - dl('startup.'.PHP_SHLIB_SUFFIX); - } - $module = 'startup'; - $functions = get_extension_funcs($module); - echo "Functions available in the test extension:$br\n"; - foreach ($functions as $func) { - echo $func."$br\n"; - } - echo "$br\n"; -} - -function main() -{ - $instance = new Minbaby\Ext\Test(); - echo $instance->xab(); -} - -main(); +echo __('Stringy')::create("testing the method")->collapseWhitespace(); diff --git a/tools/build.sh b/tools/build.sh deleted file mode 100755 index f9ce3c6..0000000 --- a/tools/build.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -set -e -set -x - -pwd - -echo "disabled startup" -phpbrew ext disable startup - -echo "go..." -phpize && ./configure && make && make install && make clean && phpize --clean - -echo "enabled startup" -phpbrew ext enable startup